From dfdc14ea8618a7242d04bd6ea066d3e6b60a10b9 Mon Sep 17 00:00:00 2001 From: Skylot Date: Tue, 26 Oct 2021 20:06:14 +0100 Subject: [PATCH] feat: rename without deobfuscation, save renames in project (#1076 #1022) --- .../src/main/java/jadx/api/ICodeWriter.java | 4 +- .../main/java/jadx/api/JadxDecompiler.java | 26 ++- .../src/main/java/jadx/api/JavaClass.java | 5 + .../src/main/java/jadx/api/JavaField.java | 5 + .../src/main/java/jadx/api/JavaMethod.java | 12 +- .../src/main/java/jadx/api/JavaNode.java | 3 + .../src/main/java/jadx/api/JavaVariable.java | 59 +++--- .../main/java/jadx/api/data/CodeRefType.java | 8 + .../main/java/jadx/api/data/ICodeComment.java | 15 +- .../main/java/jadx/api/data/ICodeData.java | 4 +- .../main/java/jadx/api/data/ICodeRename.java | 13 ++ .../main/java/jadx/api/data/IJavaCodeRef.java | 15 ++ .../main/java/jadx/api/data/IJavaNodeRef.java | 2 +- .../api/data/annotations/CustomOffsetRef.java | 27 --- .../api/data/annotations/VarDeclareRef.java | 57 ++++++ .../jadx/api/data/annotations/VarRef.java | 98 ++++++++++ .../jadx/api/data/impl/JadxCodeComment.java | 65 +++---- .../java/jadx/api/data/impl/JadxCodeData.java | 30 +--- .../java/jadx/api/data/impl/JadxCodeRef.java | 88 +++++++++ .../jadx/api/data/impl/JadxCodeRename.java | 88 +++++++++ .../java/jadx/api/data/impl/JadxNodeRef.java | 15 +- .../jadx/api/impl/AnnotatedCodeWriter.java | 18 +- .../java/jadx/api/impl/SimpleCodeWriter.java | 4 +- jadx-core/src/main/java/jadx/core/Jadx.java | 4 +- .../main/java/jadx/core/codegen/InsnGen.java | 34 +--- .../java/jadx/core/codegen/MethodGen.java | 18 +- .../java/jadx/core/codegen/RegionGen.java | 46 +---- .../java/jadx/core/deobf/DeobfPresets.java | 47 +---- .../java/jadx/core/deobf/Deobfuscator.java | 35 +++- .../java/jadx/core/deobf/PackageNode.java | 9 +- .../dex/attributes/ILineAttributeNode.java | 15 ++ .../dex/attributes/nodes/LineAttrNode.java | 9 +- .../java/jadx/core/dex/info/ClassInfo.java | 11 ++ .../java/jadx/core/dex/info/FieldInfo.java | 4 + .../java/jadx/core/dex/info/MethodInfo.java | 32 +--- .../core/dex/instructions/args/CodeVar.java | 34 ++-- .../core/dex/instructions/args/NamedArg.java | 14 +- .../dex/instructions/args/VisibleVar.java | 14 -- .../dex/nodes/ICodeDataUpdateListener.java | 8 + .../java/jadx/core/dex/nodes/MethodNode.java | 47 ----- .../java/jadx/core/dex/nodes/RootNode.java | 11 ++ .../jadx/core/dex/nodes/VariableNode.java | 96 ---------- .../dex/visitors/AttachCommentsVisitor.java | 81 ++++----- .../dex/visitors/OverrideMethodVisitor.java | 1 + .../visitors/rename/CodeRenameVisitor.java | 125 +++++++++++++ .../visitors/{ => rename}/RenameVisitor.java | 5 +- .../core/dex/visitors/rename/UserRenames.java | 132 ++++++++++++++ .../dex/visitors/usage/UsageInfoVisitor.java | 2 +- .../java/jadx/core/utils/DebugChecks.java | 2 +- .../utils/assertj/JadxCodeInfoAssertions.java | 2 +- .../integration/others/TestCodeComments.java | 13 +- .../integration/others/TestCodeComments2.java | 8 +- .../others/TestCodeComments2a.java | 9 +- .../others/TestCodeCommentsMultiline.java | 5 +- .../others/TestCodeCommentsOverride.java | 4 +- .../integration/rename/TestUserRenames.java | 91 ++++++++++ .../java/jadx/gui/settings/JadxProject.java | 6 + .../java/jadx/gui/treemodel/JVariable.java | 27 +-- .../src/main/java/jadx/gui/ui/MainWindow.java | 10 +- .../java/jadx/gui/ui/codearea/CodeArea.java | 32 ++++ .../jadx/gui/ui/codearea/CommentAction.java | 10 +- .../ui/codearea/MouseHoverHighlighter.java | 14 ++ .../jadx/gui/ui/codearea/RenameAction.java | 7 +- .../jadx/gui/ui/dialog/CommentDialog.java | 7 +- .../java/jadx/gui/ui/dialog/RenameDialog.java | 170 +++++++----------- .../main/java/jadx/gui/utils/JNodeCache.java | 4 +- .../jadx/gui/utils/search/CommentsIndex.java | 6 +- .../resources/i18n/Messages_de_DE.properties | 4 - .../resources/i18n/Messages_en_US.properties | 4 - .../resources/i18n/Messages_es_ES.properties | 4 - .../resources/i18n/Messages_ko_KR.properties | 4 - .../resources/i18n/Messages_zh_CN.properties | 4 - 72 files changed, 1221 insertions(+), 721 deletions(-) create mode 100644 jadx-core/src/main/java/jadx/api/data/CodeRefType.java create mode 100644 jadx-core/src/main/java/jadx/api/data/ICodeRename.java create mode 100644 jadx-core/src/main/java/jadx/api/data/IJavaCodeRef.java delete mode 100644 jadx-core/src/main/java/jadx/api/data/annotations/CustomOffsetRef.java create mode 100644 jadx-core/src/main/java/jadx/api/data/annotations/VarDeclareRef.java create mode 100644 jadx-core/src/main/java/jadx/api/data/annotations/VarRef.java create mode 100644 jadx-core/src/main/java/jadx/api/data/impl/JadxCodeRef.java create mode 100644 jadx-core/src/main/java/jadx/api/data/impl/JadxCodeRename.java create mode 100644 jadx-core/src/main/java/jadx/core/dex/attributes/ILineAttributeNode.java delete mode 100644 jadx-core/src/main/java/jadx/core/dex/instructions/args/VisibleVar.java create mode 100644 jadx-core/src/main/java/jadx/core/dex/nodes/ICodeDataUpdateListener.java delete mode 100644 jadx-core/src/main/java/jadx/core/dex/nodes/VariableNode.java create mode 100644 jadx-core/src/main/java/jadx/core/dex/visitors/rename/CodeRenameVisitor.java rename jadx-core/src/main/java/jadx/core/dex/visitors/{ => rename}/RenameVisitor.java (98%) create mode 100644 jadx-core/src/main/java/jadx/core/dex/visitors/rename/UserRenames.java create mode 100644 jadx-core/src/test/java/jadx/tests/integration/rename/TestUserRenames.java diff --git a/jadx-core/src/main/java/jadx/api/ICodeWriter.java b/jadx-core/src/main/java/jadx/api/ICodeWriter.java index 2fae589f2..e1facd5c1 100644 --- a/jadx-core/src/main/java/jadx/api/ICodeWriter.java +++ b/jadx-core/src/main/java/jadx/api/ICodeWriter.java @@ -2,7 +2,7 @@ package jadx.api; import java.util.Map; -import jadx.core.dex.attributes.nodes.LineAttrNode; +import jadx.core.dex.attributes.ILineAttributeNode; public interface ICodeWriter { String NL = System.getProperty("line.separator"); @@ -40,7 +40,7 @@ public interface ICodeWriter { int getLine(); - void attachDefinition(LineAttrNode obj); + void attachDefinition(ILineAttributeNode obj); void attachAnnotation(Object obj); diff --git a/jadx-core/src/main/java/jadx/api/JadxDecompiler.java b/jadx-core/src/main/java/jadx/api/JadxDecompiler.java index e1dc1a200..2ec3bcb0d 100644 --- a/jadx-core/src/main/java/jadx/api/JadxDecompiler.java +++ b/jadx-core/src/main/java/jadx/api/JadxDecompiler.java @@ -24,6 +24,7 @@ import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import jadx.api.data.annotations.VarRef; import jadx.api.plugins.JadxPlugin; import jadx.api.plugins.JadxPluginManager; import jadx.api.plugins.input.JadxInputPlugin; @@ -35,7 +36,6 @@ 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.nodes.VariableNode; import jadx.core.dex.visitors.SaveCode; import jadx.core.export.ExportGradleProject; import jadx.core.utils.Utils; @@ -439,6 +439,9 @@ public final class JadxDecompiler implements Closeable { if (javaMethod != null) { return javaMethod; } + if (mth.contains(AFlag.DONT_GENERATE)) { + return null; + } // parent class not loaded yet JavaClass javaClass = getJavaClassByNode(mth.getParentClass().getTopParentClass()); if (javaClass == null) { @@ -522,6 +525,15 @@ public final class JadxDecompiler implements Closeable { @Nullable JavaNode convertNode(Object obj) { + if (obj instanceof VarRef) { + VarRef varRef = (VarRef) obj; + MethodNode mthNode = varRef.getMth(); + JavaMethod mth = getJavaMethodByNode(mthNode); + if (mth == null) { + return null; + } + return new JavaVariable(mth, varRef); + } if (!(obj instanceof LineAttrNode)) { return null; } @@ -538,14 +550,6 @@ public final class JadxDecompiler implements Closeable { if (obj instanceof FieldNode) { return getJavaFieldByNode((FieldNode) obj); } - if (obj instanceof VariableNode) { - VariableNode varNode = (VariableNode) obj; - JavaClass javaCls = getJavaClassByNode(varNode.getClassNode().getTopParentClass()); - if (javaCls == null) { - return null; - } - return new JavaVariable(javaCls, varNode); - } throw new JadxRuntimeException("Unexpected node type: " + obj); } @@ -580,6 +584,10 @@ public final class JadxDecompiler implements Closeable { return new CodePosition(defLine, 0, javaNode.getDefPos()); } + public void reloadCodeData() { + root.notifyCodeDataListeners(); + } + public JadxArgs getArgs() { return args; } diff --git a/jadx-core/src/main/java/jadx/api/JavaClass.java b/jadx-core/src/main/java/jadx/api/JavaClass.java index 30f521f56..d1b41c371 100644 --- a/jadx-core/src/main/java/jadx/api/JavaClass.java +++ b/jadx-core/src/main/java/jadx/api/JavaClass.java @@ -248,6 +248,11 @@ public final class JavaClass implements JavaNode { return new JavaMethod(this, methodNode); } + @Override + public void removeAlias() { + this.cls.getClassInfo().removeAlias(); + } + @Override public int getDecompiledLine() { return cls.getDecompiledLine(); diff --git a/jadx-core/src/main/java/jadx/api/JavaField.java b/jadx-core/src/main/java/jadx/api/JavaField.java index 5dd747048..12a440b5d 100644 --- a/jadx-core/src/main/java/jadx/api/JavaField.java +++ b/jadx-core/src/main/java/jadx/api/JavaField.java @@ -59,6 +59,11 @@ public final class JavaField implements JavaNode { return getDeclaringClass().getRootDecompiler().convertNodes(field.getUseIn()); } + @Override + public void removeAlias() { + this.field.getFieldInfo().removeAlias(); + } + /** * Internal API. Not Stable! */ diff --git a/jadx-core/src/main/java/jadx/api/JavaMethod.java b/jadx-core/src/main/java/jadx/api/JavaMethod.java index 80110c845..277a8889a 100644 --- a/jadx-core/src/main/java/jadx/api/JavaMethod.java +++ b/jadx-core/src/main/java/jadx/api/JavaMethod.java @@ -2,6 +2,7 @@ package jadx.api; import java.util.Collections; import java.util.List; +import java.util.stream.Collectors; import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.nodes.MethodOverrideAttr; @@ -63,13 +64,15 @@ public final class JavaMethod implements JavaNode { return getDeclaringClass().getRootDecompiler().convertNodes(mth.getUseIn()); } - public List getOverrideRelatedMethods() { + public List getOverrideRelatedMethods() { MethodOverrideAttr ovrdAttr = mth.get(AType.METHOD_OVERRIDE); if (ovrdAttr == null) { return Collections.emptyList(); } JadxDecompiler decompiler = getDeclaringClass().getRootDecompiler(); - return decompiler.convertNodes(ovrdAttr.getRelatedMthNodes()); + return ovrdAttr.getRelatedMthNodes().stream() + .map(m -> ((JavaMethod) decompiler.convertNode(m))) + .collect(Collectors.toList()); } public boolean isConstructor() { @@ -90,6 +93,11 @@ public final class JavaMethod implements JavaNode { return mth.getDefPosition(); } + @Override + public void removeAlias() { + this.mth.getMethodInfo().removeAlias(); + } + /** * Internal API. Not Stable! */ diff --git a/jadx-core/src/main/java/jadx/api/JavaNode.java b/jadx-core/src/main/java/jadx/api/JavaNode.java index d8887214b..c126ebc11 100644 --- a/jadx-core/src/main/java/jadx/api/JavaNode.java +++ b/jadx-core/src/main/java/jadx/api/JavaNode.java @@ -17,4 +17,7 @@ public interface JavaNode { int getDefPos(); List getUseIn(); + + default void removeAlias() { + } } diff --git a/jadx-core/src/main/java/jadx/api/JavaVariable.java b/jadx-core/src/main/java/jadx/api/JavaVariable.java index 1b553f04a..d757fac0d 100644 --- a/jadx-core/src/main/java/jadx/api/JavaVariable.java +++ b/jadx-core/src/main/java/jadx/api/JavaVariable.java @@ -2,68 +2,85 @@ package jadx.api; import java.util.Collections; import java.util.List; -import java.util.Objects; -import jadx.core.dex.nodes.VariableNode; +import jadx.api.data.annotations.VarDeclareRef; +import jadx.api.data.annotations.VarRef; public class JavaVariable implements JavaNode { - private final JavaClass cls; - private final VariableNode node; + private final JavaMethod mth; + private final VarRef varRef; - public JavaVariable(JavaClass cls, VariableNode node) { - this.cls = Objects.requireNonNull(cls); - this.node = Objects.requireNonNull(node); + public JavaVariable(JavaMethod mth, VarRef varRef) { + this.mth = mth; + this.varRef = varRef; } - public VariableNode getVariableNode() { - return node; + public JavaMethod getMth() { + return mth; + } + + public int getReg() { + return varRef.getReg(); + } + + public int getSsa() { + return varRef.getSsa(); } @Override public String getName() { - return node.getName(); + return varRef.getName(); } @Override public String getFullName() { - return node.getName(); + return varRef.getType() + " " + varRef.getName() + " (r" + varRef.getReg() + "v" + varRef.getSsa() + ")"; } @Override public JavaClass getDeclaringClass() { - return cls; + return mth.getDeclaringClass(); } @Override public JavaClass getTopParentClass() { - return cls.getTopParentClass(); + return mth.getTopParentClass(); } @Override public int getDecompiledLine() { - return node.getDecompiledLine(); + if (varRef instanceof VarDeclareRef) { + return ((VarDeclareRef) varRef).getDecompiledLine(); + } + return 0; } @Override public int getDefPos() { - return node.getDefPosition(); + if (varRef instanceof VarDeclareRef) { + return ((VarDeclareRef) varRef).getDefPosition(); + } + return 0; } @Override public List getUseIn() { - return Collections.emptyList(); + return Collections.singletonList(mth); } @Override public int hashCode() { - return node.hashCode(); + return varRef.hashCode(); } @Override - public boolean equals(Object obj) { - if (obj instanceof JavaVariable) { - return node.equals(((JavaVariable) obj).getVariableNode()); + public boolean equals(Object o) { + if (this == o) { + return true; } - return false; + if (!(o instanceof JavaVariable)) { + return false; + } + return varRef.equals(((JavaVariable) o).varRef); } } diff --git a/jadx-core/src/main/java/jadx/api/data/CodeRefType.java b/jadx-core/src/main/java/jadx/api/data/CodeRefType.java new file mode 100644 index 000000000..e08921f06 --- /dev/null +++ b/jadx-core/src/main/java/jadx/api/data/CodeRefType.java @@ -0,0 +1,8 @@ +package jadx.api.data; + +public enum CodeRefType { + MTH_ARG, + VAR, + CATCH, + INSN, +} diff --git a/jadx-core/src/main/java/jadx/api/data/ICodeComment.java b/jadx-core/src/main/java/jadx/api/data/ICodeComment.java index a817f39e7..eace22bac 100644 --- a/jadx-core/src/main/java/jadx/api/data/ICodeComment.java +++ b/jadx-core/src/main/java/jadx/api/data/ICodeComment.java @@ -6,17 +6,8 @@ public interface ICodeComment extends Comparable { IJavaNodeRef getNodeRef(); - String getComment(); - - /** - * Instruction offset inside method - */ - int getOffset(); - - enum AttachType { - VAR_DECLARE - } - @Nullable - AttachType getAttachType(); + IJavaCodeRef getCodeRef(); + + String getComment(); } diff --git a/jadx-core/src/main/java/jadx/api/data/ICodeData.java b/jadx-core/src/main/java/jadx/api/data/ICodeData.java index 9dc193ae5..17518359d 100644 --- a/jadx-core/src/main/java/jadx/api/data/ICodeData.java +++ b/jadx-core/src/main/java/jadx/api/data/ICodeData.java @@ -4,7 +4,7 @@ import java.util.List; public interface ICodeData { - long getUpdateId(); - List getComments(); + + List getRenames(); } diff --git a/jadx-core/src/main/java/jadx/api/data/ICodeRename.java b/jadx-core/src/main/java/jadx/api/data/ICodeRename.java new file mode 100644 index 000000000..07e2f3d19 --- /dev/null +++ b/jadx-core/src/main/java/jadx/api/data/ICodeRename.java @@ -0,0 +1,13 @@ +package jadx.api.data; + +import org.jetbrains.annotations.Nullable; + +public interface ICodeRename extends Comparable { + + IJavaNodeRef getNodeRef(); + + @Nullable + IJavaCodeRef getCodeRef(); + + String getNewName(); +} diff --git a/jadx-core/src/main/java/jadx/api/data/IJavaCodeRef.java b/jadx-core/src/main/java/jadx/api/data/IJavaCodeRef.java new file mode 100644 index 000000000..6174bf235 --- /dev/null +++ b/jadx-core/src/main/java/jadx/api/data/IJavaCodeRef.java @@ -0,0 +1,15 @@ +package jadx.api.data; + +import org.jetbrains.annotations.NotNull; + +public interface IJavaCodeRef extends Comparable { + + CodeRefType getAttachType(); + + int getIndex(); + + @Override + default int compareTo(@NotNull IJavaCodeRef o) { + return Integer.compare(getIndex(), o.getIndex()); + } +} diff --git a/jadx-core/src/main/java/jadx/api/data/IJavaNodeRef.java b/jadx-core/src/main/java/jadx/api/data/IJavaNodeRef.java index f3c775bbb..6672e4b35 100644 --- a/jadx-core/src/main/java/jadx/api/data/IJavaNodeRef.java +++ b/jadx-core/src/main/java/jadx/api/data/IJavaNodeRef.java @@ -3,7 +3,7 @@ package jadx.api.data; public interface IJavaNodeRef extends Comparable { enum RefType { - CLASS, FIELD, METHOD + CLASS, FIELD, METHOD, PKG } RefType getType(); diff --git a/jadx-core/src/main/java/jadx/api/data/annotations/CustomOffsetRef.java b/jadx-core/src/main/java/jadx/api/data/annotations/CustomOffsetRef.java deleted file mode 100644 index 239561db5..000000000 --- a/jadx-core/src/main/java/jadx/api/data/annotations/CustomOffsetRef.java +++ /dev/null @@ -1,27 +0,0 @@ -package jadx.api.data.annotations; - -import jadx.api.data.ICodeComment; - -public class CustomOffsetRef implements ICodeRawOffset { - private final int offset; - private final ICodeComment.AttachType attachType; - - public CustomOffsetRef(int offset, ICodeComment.AttachType attachType) { - this.offset = offset; - this.attachType = attachType; - } - - @Override - public int getOffset() { - return offset; - } - - public ICodeComment.AttachType getAttachType() { - return attachType; - } - - @Override - public String toString() { - return "CustomOffsetRef{offset=" + offset + ", attachType=" + attachType + '}'; - } -} diff --git a/jadx-core/src/main/java/jadx/api/data/annotations/VarDeclareRef.java b/jadx-core/src/main/java/jadx/api/data/annotations/VarDeclareRef.java new file mode 100644 index 000000000..fff5f083e --- /dev/null +++ b/jadx-core/src/main/java/jadx/api/data/annotations/VarDeclareRef.java @@ -0,0 +1,57 @@ +package jadx.api.data.annotations; + +import jadx.core.dex.attributes.ILineAttributeNode; +import jadx.core.dex.instructions.args.CodeVar; +import jadx.core.dex.nodes.MethodNode; + +public class VarDeclareRef extends VarRef implements ILineAttributeNode { + + public static VarDeclareRef get(MethodNode mth, CodeVar codeVar) { + VarDeclareRef ref = new VarDeclareRef(mth, codeVar); + codeVar.setCachedVarRef(ref); + return ref; + } + + private int sourceLine; + private int decompiledLine; + private int defPosition; + + private VarDeclareRef(MethodNode mth, CodeVar codeVar) { + super(mth, codeVar.getAnySsaVar()); + } + + @Override + public int getSourceLine() { + return sourceLine; + } + + @Override + public void setSourceLine(int sourceLine) { + this.sourceLine = sourceLine; + } + + @Override + public int getDecompiledLine() { + return decompiledLine; + } + + @Override + public void setDecompiledLine(int decompiledLine) { + this.decompiledLine = decompiledLine; + } + + @Override + public int getDefPosition() { + return defPosition; + } + + @Override + public void setDefPosition(int pos) { + this.defPosition = pos; + } + + @Override + public String toString() { + return "VarDeclareRef{r" + getReg() + 'v' + getSsa() + '}'; + } +} diff --git a/jadx-core/src/main/java/jadx/api/data/annotations/VarRef.java b/jadx-core/src/main/java/jadx/api/data/annotations/VarRef.java new file mode 100644 index 000000000..1612c109f --- /dev/null +++ b/jadx-core/src/main/java/jadx/api/data/annotations/VarRef.java @@ -0,0 +1,98 @@ +package jadx.api.data.annotations; + +import org.jetbrains.annotations.Nullable; + +import jadx.core.dex.instructions.args.ArgType; +import jadx.core.dex.instructions.args.CodeVar; +import jadx.core.dex.instructions.args.RegisterArg; +import jadx.core.dex.instructions.args.SSAVar; +import jadx.core.dex.nodes.MethodNode; + +public class VarRef { + + @Nullable + public static VarRef get(MethodNode mth, RegisterArg reg) { + SSAVar ssaVar = reg.getSVar(); + if (ssaVar == null) { + return null; + } + CodeVar codeVar = ssaVar.getCodeVar(); + VarRef cachedVarRef = codeVar.getCachedVarRef(); + if (cachedVarRef != null) { + if (cachedVarRef.getName() == null) { + cachedVarRef.setName(codeVar.getName()); + } + return cachedVarRef; + } + VarRef newVarRef = new VarRef(mth, ssaVar); + codeVar.setCachedVarRef(newVarRef); + return newVarRef; + } + + private final MethodNode mth; + private final int reg; + private final int ssa; + private final ArgType type; + private String name; + + protected VarRef(MethodNode mth, SSAVar ssaVar) { + this(mth, ssaVar.getRegNum(), ssaVar.getVersion(), + ssaVar.getCodeVar().getType(), ssaVar.getCodeVar().getName()); + } + + private VarRef(MethodNode mth, int reg, int ssa, ArgType type, String name) { + this.mth = mth; + this.reg = reg; + this.ssa = ssa; + this.type = type; + this.name = name; + } + + public MethodNode getMth() { + return mth; + } + + public int getReg() { + return reg; + } + + public int getSsa() { + return ssa; + } + + public ArgType getType() { + return type; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof VarRef)) { + return false; + } + VarRef other = (VarRef) o; + return getReg() == other.getReg() + && getSsa() == other.getSsa() + && getMth().equals(other.getMth()); + } + + @Override + public int hashCode() { + return 31 * getReg() + getSsa(); + } + + @Override + public String toString() { + return "VarUseRef{r" + reg + 'v' + ssa + '}'; + } +} diff --git a/jadx-core/src/main/java/jadx/api/data/impl/JadxCodeComment.java b/jadx-core/src/main/java/jadx/api/data/impl/JadxCodeComment.java index 5226607e9..8092cf41b 100644 --- a/jadx-core/src/main/java/jadx/api/data/impl/JadxCodeComment.java +++ b/jadx-core/src/main/java/jadx/api/data/impl/JadxCodeComment.java @@ -1,32 +1,27 @@ package jadx.api.data.impl; -import java.util.Comparator; - import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import jadx.api.data.ICodeComment; +import jadx.api.data.IJavaCodeRef; import jadx.api.data.IJavaNodeRef; public class JadxCodeComment implements ICodeComment { private IJavaNodeRef nodeRef; + @Nullable + private IJavaCodeRef codeRef; private String comment; - private int offset; - private AttachType attachType; public JadxCodeComment(IJavaNodeRef nodeRef, String comment) { - this(nodeRef, comment, -1, null); + this(nodeRef, null, comment); } - public JadxCodeComment(IJavaNodeRef nodeRef, String comment, int offset) { - this(nodeRef, comment, offset, null); - } - - public JadxCodeComment(IJavaNodeRef nodeRef, String comment, int offset, AttachType attachType) { + public JadxCodeComment(IJavaNodeRef nodeRef, @Nullable IJavaCodeRef codeRef, String comment) { this.nodeRef = nodeRef; + this.codeRef = codeRef; this.comment = comment; - this.offset = offset; - this.attachType = attachType; } public JadxCodeComment() { @@ -42,6 +37,16 @@ public class JadxCodeComment implements ICodeComment { this.nodeRef = nodeRef; } + @Nullable + @Override + public IJavaCodeRef getCodeRef() { + return codeRef; + } + + public void setCodeRef(@Nullable IJavaCodeRef codeRef) { + this.codeRef = codeRef; + } + @Override public String getComment() { return comment; @@ -51,35 +56,23 @@ public class JadxCodeComment implements ICodeComment { this.comment = comment; } - @Override - public int getOffset() { - return offset; - } - - public void setOffset(int offset) { - this.offset = offset; - } - - @Override - public AttachType getAttachType() { - return attachType; - } - - public void setAttachType(AttachType attachType) { - this.attachType = attachType; - } - - private static final Comparator COMPARATOR = Comparator - .comparing(ICodeComment::getNodeRef) - .thenComparing(ICodeComment::getOffset); - @Override public int compareTo(@NotNull ICodeComment other) { - return COMPARATOR.compare(this, other); + int cmpNodeRef = this.getNodeRef().compareTo(other.getNodeRef()); + if (cmpNodeRef != 0) { + return cmpNodeRef; + } + if (this.getCodeRef() != null && other.getCodeRef() != null) { + return this.getCodeRef().compareTo(other.getCodeRef()); + } + return this.getComment().compareTo(other.getComment()); } @Override public String toString() { - return "JadxCodeComment{" + nodeRef + ", comment='" + comment + '\'' + ", offset=" + offset + '}'; + return "JadxCodeComment{" + nodeRef + + ", ref=" + codeRef + + ", comment='" + comment + '\'' + + '}'; } } diff --git a/jadx-core/src/main/java/jadx/api/data/impl/JadxCodeData.java b/jadx-core/src/main/java/jadx/api/data/impl/JadxCodeData.java index 8b736a5f0..ccc60e6e6 100644 --- a/jadx-core/src/main/java/jadx/api/data/impl/JadxCodeData.java +++ b/jadx-core/src/main/java/jadx/api/data/impl/JadxCodeData.java @@ -5,20 +5,11 @@ import java.util.List; import jadx.api.data.ICodeComment; import jadx.api.data.ICodeData; +import jadx.api.data.ICodeRename; public class JadxCodeData implements ICodeData { - - private long updateId = System.currentTimeMillis(); private List comments = Collections.emptyList(); - - @Override - public long getUpdateId() { - return updateId; - } - - public void markUpdate() { - updateId = System.currentTimeMillis(); - } + private List renames = Collections.emptyList(); @Override public List getComments() { @@ -26,24 +17,15 @@ public class JadxCodeData implements ICodeData { } public void setComments(List comments) { - markUpdate(); this.comments = comments; } @Override - public int hashCode() { - return Long.hashCode(updateId); + public List getRenames() { + return renames; } - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof JadxCodeData)) { - return false; - } - JadxCodeData that = (JadxCodeData) o; - return updateId == that.updateId; + public void setRenames(List renames) { + this.renames = renames; } } diff --git a/jadx-core/src/main/java/jadx/api/data/impl/JadxCodeRef.java b/jadx-core/src/main/java/jadx/api/data/impl/JadxCodeRef.java new file mode 100644 index 000000000..17ebe1c3c --- /dev/null +++ b/jadx-core/src/main/java/jadx/api/data/impl/JadxCodeRef.java @@ -0,0 +1,88 @@ +package jadx.api.data.impl; + +import jadx.api.JavaVariable; +import jadx.api.data.CodeRefType; +import jadx.api.data.IJavaCodeRef; +import jadx.api.data.annotations.VarRef; + +public class JadxCodeRef implements IJavaCodeRef { + + public static JadxCodeRef forInsn(int offset) { + return new JadxCodeRef(CodeRefType.INSN, offset); + } + + public static JadxCodeRef forMthArg(int argIndex) { + return new JadxCodeRef(CodeRefType.MTH_ARG, argIndex); + } + + public static JadxCodeRef forVar(int regNum, int ssaVersion) { + return new JadxCodeRef(CodeRefType.VAR, regNum << 16 | ssaVersion); + } + + public static JadxCodeRef forVar(JavaVariable javaVariable) { + return forVar(javaVariable.getReg(), javaVariable.getSsa()); + } + + public static JadxCodeRef forVar(VarRef varRef) { + return forVar(varRef.getReg(), varRef.getSsa()); + } + + public static JadxCodeRef forCatch(int handlerOffset) { + return new JadxCodeRef(CodeRefType.CATCH, handlerOffset); + } + + private CodeRefType attachType; + private int index; + + public JadxCodeRef(CodeRefType attachType, int index) { + this.attachType = attachType; + this.index = index; + } + + public JadxCodeRef() { + // used for json serialization + } + + public CodeRefType getAttachType() { + return attachType; + } + + public void setAttachType(CodeRefType attachType) { + this.attachType = attachType; + } + + @Override + public int getIndex() { + return index; + } + + public void setIndex(int index) { + this.index = index; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof JadxCodeRef)) { + return false; + } + JadxCodeRef other = (JadxCodeRef) o; + return getIndex() == other.getIndex() + && getAttachType() == other.getAttachType(); + } + + @Override + public int hashCode() { + return 31 * getAttachType().hashCode() + getIndex(); + } + + @Override + public String toString() { + return "JadxCodeRef{" + + "attachType=" + attachType + + ", index=" + index + + '}'; + } +} diff --git a/jadx-core/src/main/java/jadx/api/data/impl/JadxCodeRename.java b/jadx-core/src/main/java/jadx/api/data/impl/JadxCodeRename.java new file mode 100644 index 000000000..2005a0fe2 --- /dev/null +++ b/jadx-core/src/main/java/jadx/api/data/impl/JadxCodeRename.java @@ -0,0 +1,88 @@ +package jadx.api.data.impl; + +import java.util.Objects; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import jadx.api.data.ICodeRename; +import jadx.api.data.IJavaCodeRef; +import jadx.api.data.IJavaNodeRef; + +public class JadxCodeRename implements ICodeRename { + private IJavaNodeRef nodeRef; + @Nullable + private IJavaCodeRef codeRef; + private String newName; + + public JadxCodeRename(IJavaNodeRef nodeRef, String newName) { + this(nodeRef, null, newName); + } + + public JadxCodeRename(IJavaNodeRef nodeRef, @Nullable IJavaCodeRef codeRef, String newName) { + this.nodeRef = nodeRef; + this.codeRef = codeRef; + this.newName = newName; + } + + public JadxCodeRename() { + // used in json serialization + } + + @Override + public IJavaNodeRef getNodeRef() { + return nodeRef; + } + + public void setNodeRef(IJavaNodeRef nodeRef) { + this.nodeRef = nodeRef; + } + + @Override + public IJavaCodeRef getCodeRef() { + return codeRef; + } + + public void setCodeRef(IJavaCodeRef codeRef) { + this.codeRef = codeRef; + } + + @Override + public String getNewName() { + return newName; + } + + public void setNewName(String newName) { + this.newName = newName; + } + + @Override + public int compareTo(@NotNull ICodeRename other) { + int cmpNodeRef = this.getNodeRef().compareTo(other.getNodeRef()); + if (cmpNodeRef != 0) { + return cmpNodeRef; + } + if (this.getCodeRef() != null && other.getCodeRef() != null) { + return this.getCodeRef().compareTo(other.getCodeRef()); + } + return this.getNewName().compareTo(other.getNewName()); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof ICodeRename)) { + return false; + } + ICodeRename other = (ICodeRename) o; + return getNodeRef().equals(other.getNodeRef()) + && Objects.equals(getCodeRef(), other.getCodeRef()); + } + + @Override + public int hashCode() { + return 31 * getNodeRef().hashCode() + Objects.hashCode(getCodeRef()); + } +} diff --git a/jadx-core/src/main/java/jadx/api/data/impl/JadxNodeRef.java b/jadx-core/src/main/java/jadx/api/data/impl/JadxNodeRef.java index 895541fb8..e2c51d790 100644 --- a/jadx-core/src/main/java/jadx/api/data/impl/JadxNodeRef.java +++ b/jadx-core/src/main/java/jadx/api/data/impl/JadxNodeRef.java @@ -29,7 +29,7 @@ public class JadxNodeRef implements IJavaNodeRef { } public static JadxNodeRef forCls(JavaClass cls) { - return new JadxNodeRef(RefType.CLASS, cls.getClassNode().getClassInfo().getFullName(), null); + return new JadxNodeRef(RefType.CLASS, getClassRefStr(cls), null); } public static JadxNodeRef forCls(String clsFullName) { @@ -38,16 +38,24 @@ public class JadxNodeRef implements IJavaNodeRef { public static JadxNodeRef forMth(JavaMethod mth) { return new JadxNodeRef(RefType.METHOD, - mth.getDeclaringClass().getClassNode().getClassInfo().getFullName(), + getClassRefStr(mth.getDeclaringClass()), mth.getMethodNode().getMethodInfo().getShortId()); } public static JadxNodeRef forFld(JavaField fld) { return new JadxNodeRef(RefType.FIELD, - fld.getDeclaringClass().getClassNode().getClassInfo().getFullName(), + getClassRefStr(fld.getDeclaringClass()), fld.getFieldNode().getFieldInfo().getShortId()); } + public static JadxNodeRef forPkg(String pkgFullName) { + return new JadxNodeRef(RefType.PKG, pkgFullName, ""); + } + + private static String getClassRefStr(JavaClass cls) { + return cls.getClassNode().getClassInfo().getRawName(); + } + private RefType refType; private String declClass; @Nullable @@ -124,6 +132,7 @@ public class JadxNodeRef implements IJavaNodeRef { public String toString() { switch (refType) { case CLASS: + case PKG: return declClass; case FIELD: case METHOD: diff --git a/jadx-core/src/main/java/jadx/api/impl/AnnotatedCodeWriter.java b/jadx-core/src/main/java/jadx/api/impl/AnnotatedCodeWriter.java index 4c5b177e7..ccb716a29 100644 --- a/jadx-core/src/main/java/jadx/api/impl/AnnotatedCodeWriter.java +++ b/jadx-core/src/main/java/jadx/api/impl/AnnotatedCodeWriter.java @@ -9,7 +9,7 @@ import jadx.api.CodePosition; import jadx.api.ICodeInfo; import jadx.api.ICodeWriter; import jadx.api.JadxArgs; -import jadx.core.dex.attributes.nodes.LineAttrNode; +import jadx.core.dex.attributes.ILineAttributeNode; import jadx.core.utils.StringUtils; public class AnnotatedCodeWriter extends SimpleCodeWriter implements ICodeWriter { @@ -102,25 +102,31 @@ public class AnnotatedCodeWriter extends SimpleCodeWriter implements ICodeWriter } private static final class DefinitionWrapper { - private final LineAttrNode node; + private final ILineAttributeNode node; - private DefinitionWrapper(LineAttrNode node) { + private DefinitionWrapper(ILineAttributeNode node) { this.node = node; } - public LineAttrNode getNode() { + public ILineAttributeNode getNode() { return node; } } @Override - public void attachDefinition(LineAttrNode obj) { + public void attachDefinition(ILineAttributeNode obj) { + if (obj == null) { + return; + } attachAnnotation(obj); attachAnnotation(new DefinitionWrapper(obj), new CodePosition(line, offset, getLength())); } @Override public void attachAnnotation(Object obj) { + if (obj == null) { + return; + } attachAnnotation(obj, new CodePosition(line, offset + 1, getLength())); } @@ -173,7 +179,7 @@ public class AnnotatedCodeWriter extends SimpleCodeWriter implements ICodeWriter annotations.entrySet().removeIf(entry -> { Object v = entry.getValue(); if (v instanceof DefinitionWrapper) { - LineAttrNode l = ((DefinitionWrapper) v).getNode(); + ILineAttributeNode l = ((DefinitionWrapper) v).getNode(); CodePosition codePos = entry.getKey(); l.setDecompiledLine(codePos.getLine()); l.setDefPosition(codePos.getPos()); diff --git a/jadx-core/src/main/java/jadx/api/impl/SimpleCodeWriter.java b/jadx-core/src/main/java/jadx/api/impl/SimpleCodeWriter.java index 4d9a1b3de..9e99b3f4d 100644 --- a/jadx-core/src/main/java/jadx/api/impl/SimpleCodeWriter.java +++ b/jadx-core/src/main/java/jadx/api/impl/SimpleCodeWriter.java @@ -10,7 +10,7 @@ import jadx.api.CodePosition; import jadx.api.ICodeInfo; import jadx.api.ICodeWriter; import jadx.api.JadxArgs; -import jadx.core.dex.attributes.nodes.LineAttrNode; +import jadx.core.dex.attributes.ILineAttributeNode; import jadx.core.utils.Utils; /** @@ -193,7 +193,7 @@ public class SimpleCodeWriter implements ICodeWriter { } @Override - public void attachDefinition(LineAttrNode obj) { + public void attachDefinition(ILineAttributeNode obj) { // no op } diff --git a/jadx-core/src/main/java/jadx/core/Jadx.java b/jadx-core/src/main/java/jadx/core/Jadx.java index 9231c3f3e..ae1d6f394 100644 --- a/jadx-core/src/main/java/jadx/core/Jadx.java +++ b/jadx-core/src/main/java/jadx/core/Jadx.java @@ -38,7 +38,6 @@ import jadx.core.dex.visitors.PrepareForCodeGen; import jadx.core.dex.visitors.ProcessAnonymous; import jadx.core.dex.visitors.ProcessInstructionsVisitor; import jadx.core.dex.visitors.ReSugarCode; -import jadx.core.dex.visitors.RenameVisitor; import jadx.core.dex.visitors.ShadowFieldVisitor; import jadx.core.dex.visitors.SignatureProcessor; import jadx.core.dex.visitors.SimplifyVisitor; @@ -54,6 +53,8 @@ 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.CodeRenameVisitor; +import jadx.core.dex.visitors.rename.RenameVisitor; import jadx.core.dex.visitors.shrink.CodeShrinkVisitor; import jadx.core.dex.visitors.ssa.SSATransform; import jadx.core.dex.visitors.typeinference.TypeInferenceVisitor; @@ -127,6 +128,7 @@ public class Jadx { if (args.isDebugInfo()) { passes.add(new DebugInfoApplyVisitor()); } + passes.add(new CodeRenameVisitor()); if (args.isInlineMethods()) { passes.add(new InlineMethods()); } diff --git a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java index 589c6d5e1..25f22ad2e 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/InsnGen.java @@ -12,6 +12,8 @@ import org.slf4j.LoggerFactory; import jadx.api.CommentsLevel; import jadx.api.ICodeWriter; import jadx.api.data.annotations.InsnCodeOffset; +import jadx.api.data.annotations.VarDeclareRef; +import jadx.api.data.annotations.VarRef; import jadx.api.plugins.input.data.MethodHandleType; import jadx.core.deobf.NameMapper; import jadx.core.dex.attributes.AFlag; @@ -45,7 +47,6 @@ import jadx.core.dex.instructions.args.InsnArg; import jadx.core.dex.instructions.args.InsnWrapArg; import jadx.core.dex.instructions.args.LiteralArg; import jadx.core.dex.instructions.args.Named; -import jadx.core.dex.instructions.args.NamedArg; import jadx.core.dex.instructions.args.RegisterArg; import jadx.core.dex.instructions.args.SSAVar; import jadx.core.dex.instructions.mods.ConstructorInsn; @@ -55,13 +56,11 @@ import jadx.core.dex.nodes.FieldNode; import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.RootNode; -import jadx.core.dex.nodes.VariableNode; import jadx.core.utils.CodeGenUtils; import jadx.core.utils.RegionUtils; import jadx.core.utils.exceptions.CodegenException; import jadx.core.utils.exceptions.JadxRuntimeException; -import static jadx.core.dex.nodes.VariableNode.VarKind; import static jadx.core.utils.android.AndroidResourcesUtils.handleAppResField; public class InsnGen { @@ -107,25 +106,16 @@ public class InsnGen { public void addArg(ICodeWriter code, InsnArg arg, Set flags) throws CodegenException { if (arg.isRegister()) { - CodeVar codeVar = CodeGenUtils.getCodeVar((RegisterArg) arg); - if (codeVar != null) { - VariableNode node = mth.getVariable(codeVar.getIndex()); - if (node != null) { - code.attachAnnotation(node); - } + RegisterArg reg = (RegisterArg) arg; + if (code.isMetadataSupported()) { + code.attachAnnotation(VarRef.get(mth, reg)); } - code.add(mgen.getNameGen().useArg((RegisterArg) arg)); + code.add(mgen.getNameGen().useArg(reg)); } else if (arg.isLiteral()) { code.add(lit((LiteralArg) arg)); } else if (arg.isInsnWrap()) { addWrappedArg(code, (InsnWrapArg) arg, flags); } else if (arg.isNamed()) { - if (arg instanceof NamedArg) { - VariableNode node = mth.getVariable(((NamedArg) arg).getIndex()); - if (node != null) { - code.attachAnnotation(node); - } - } code.add(((Named) arg).getName()); } else { throw new CodegenException("Unknown arg type " + arg); @@ -162,16 +152,10 @@ public class InsnGen { } useType(code, codeVar.getType()); code.add(' '); - VariableNode node = mth.declareVar(codeVar, mgen.getNameGen(), VarKind.VAR); - String name; - if (node != null) { - code.attachDefinition(node); - name = node.getName(); - codeVar.setName(name); - } else { - name = mgen.getNameGen().assignArg(codeVar); + if (code.isMetadataSupported()) { + code.attachDefinition(VarDeclareRef.get(mth, codeVar)); } - code.add(name); + code.add(mgen.getNameGen().assignArg(codeVar)); } private String lit(LiteralArg arg) { diff --git a/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java b/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java index 3233c7371..c697980bd 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/MethodGen.java @@ -12,6 +12,7 @@ import org.slf4j.LoggerFactory; import jadx.api.CommentsLevel; import jadx.api.ICodeWriter; import jadx.api.data.annotations.InsnCodeOffset; +import jadx.api.data.annotations.VarDeclareRef; import jadx.api.plugins.input.data.AccessFlags; import jadx.api.plugins.input.data.annotations.EncodedValue; import jadx.api.plugins.input.data.attributes.JadxAttrType; @@ -33,7 +34,6 @@ import jadx.core.dex.instructions.args.RegisterArg; import jadx.core.dex.instructions.args.SSAVar; import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; -import jadx.core.dex.nodes.VariableNode; import jadx.core.dex.trycatch.CatchAttr; import jadx.core.dex.visitors.DepthTraversal; import jadx.core.dex.visitors.IDexTreeVisitor; @@ -46,7 +46,6 @@ import jadx.core.utils.exceptions.JadxOverflowException; import static jadx.core.codegen.MethodGen.FallbackOption.BLOCK_DUMP; import static jadx.core.codegen.MethodGen.FallbackOption.COMMENTED_DUMP; import static jadx.core.codegen.MethodGen.FallbackOption.FALLBACK_MODE; -import static jadx.core.dex.nodes.VariableNode.VarKind; public class MethodGen { private static final Logger LOG = LoggerFactory.getLogger(MethodGen.class); @@ -228,19 +227,10 @@ public class MethodGen { classGen.useType(code, argType); } code.add(' '); - VariableNode node = mth.declareVar(var, nameGen, VarKind.ARG); - String name; - if (node != null) { - code.attachDefinition(node); - name = node.getName(); - var.setName(name); - } else { - name = nameGen.assignArg(var); + if (code.isMetadataSupported() && ssaVar != null) { + code.attachAnnotation(VarDeclareRef.get(mth, var)); } - if (var.isThis()) { - code.attachDefinition(mth.getParentClass()); - } - code.add(name); + code.add(nameGen.assignArg(var)); i++; if (it.hasNext()) { diff --git a/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java b/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java index a8dd31951..5943c20b9 100644 --- a/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java +++ b/jadx-core/src/main/java/jadx/core/codegen/RegionGen.java @@ -10,9 +10,8 @@ import org.slf4j.LoggerFactory; import jadx.api.CommentsLevel; import jadx.api.ICodeWriter; -import jadx.api.data.ICodeComment; -import jadx.api.data.annotations.CustomOffsetRef; import jadx.api.data.annotations.InsnCodeOffset; +import jadx.api.data.annotations.VarDeclareRef; import jadx.api.plugins.input.data.annotations.EncodedValue; import jadx.api.plugins.input.data.attributes.JadxAttrType; import jadx.core.dex.attributes.AFlag; @@ -32,7 +31,6 @@ import jadx.core.dex.nodes.FieldNode; import jadx.core.dex.nodes.IBlock; import jadx.core.dex.nodes.IContainer; import jadx.core.dex.nodes.InsnNode; -import jadx.core.dex.nodes.VariableNode; import jadx.core.dex.regions.Region; import jadx.core.dex.regions.SwitchRegion; import jadx.core.dex.regions.SwitchRegion.CaseInfo; @@ -52,8 +50,6 @@ import jadx.core.utils.Utils; import jadx.core.utils.exceptions.CodegenException; import jadx.core.utils.exceptions.JadxRuntimeException; -import static jadx.core.dex.nodes.VariableNode.VarKind; - public class RegionGen extends InsnGen { private static final Logger LOG = LoggerFactory.getLogger(RegionGen.class); @@ -73,23 +69,11 @@ public class RegionGen extends InsnGen { code.startLine(); declareVar(code, v); code.add(';'); - attachVariableCommentsData(code, v); + CodeGenUtils.addCodeComments(code, mth, v.getAnySsaVar().getAssign()); } } } - private void attachVariableCommentsData(ICodeWriter code, CodeVar v) { - RegisterArg assignReg = v.getSsaVars().get(0).getAssign(); - if (code.isMetadataSupported()) { - InsnNode parentInsn = assignReg.getParentInsn(); - if (parentInsn != null) { - int offset = parentInsn.getOffset(); - code.attachLineAnnotation(new CustomOffsetRef(offset, ICodeComment.AttachType.VAR_DECLARE)); - } - } - CodeGenUtils.addCodeComments(code, mth, assignReg); - } - private void makeRegionIndent(ICodeWriter code, IContainer region) throws CodegenException { code.incIndent(); makeRegion(code, region); @@ -362,29 +346,13 @@ public class RegionGen extends InsnGen { if (arg == null) { code.add("unknown"); // throwing exception is too late at this point } else if (arg instanceof RegisterArg) { - String name; - CodeVar codeVar = CodeGenUtils.getCodeVar((RegisterArg) arg); - if (codeVar != null) { - VariableNode node = mth.declareVar(codeVar, mgen.getNameGen(), VarKind.CATCH_ARG); - if (node != null) { - code.attachDefinition(node); - name = node.getName(); - codeVar.setName(name); - } else { - name = mgen.getNameGen().assignArg(codeVar); - } - } else { - RegisterArg reg = (RegisterArg) arg; - name = mgen.getNameGen().assignArg(reg.getSVar().getCodeVar()); + CodeVar codeVar = ((RegisterArg) arg).getSVar().getCodeVar(); + if (code.isMetadataSupported()) { + code.attachAnnotation(VarDeclareRef.get(mth, codeVar)); } - code.add(name); + code.add(mgen.getNameGen().assignArg(codeVar)); } else if (arg instanceof NamedArg) { - VariableNode node = mth.declareVar((NamedArg) arg, mgen.getNameGen(), VarKind.CATCH_ARG); - if (node != null) { - code.add(node.getName()); - } else { - code.add(mgen.getNameGen().assignNamedArg((NamedArg) arg)); - } + code.add(mgen.getNameGen().assignNamedArg((NamedArg) arg)); } else { throw new JadxRuntimeException("Unexpected arg type in catch block: " + arg + ", class: " + arg.getClass().getSimpleName()); } diff --git a/jadx-core/src/main/java/jadx/core/deobf/DeobfPresets.java b/jadx-core/src/main/java/jadx/core/deobf/DeobfPresets.java index 639654a55..56634b0b7 100644 --- a/jadx-core/src/main/java/jadx/core/deobf/DeobfPresets.java +++ b/jadx-core/src/main/java/jadx/core/deobf/DeobfPresets.java @@ -9,10 +9,8 @@ import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; @@ -23,7 +21,6 @@ import jadx.core.dex.info.ClassInfo; import jadx.core.dex.info.FieldInfo; import jadx.core.dex.info.MethodInfo; import jadx.core.dex.nodes.RootNode; -import jadx.core.dex.nodes.VariableNode; import jadx.core.utils.files.FileUtils; import static java.nio.charset.StandardCharsets.UTF_8; @@ -39,7 +36,6 @@ public class DeobfPresets { private final Map clsPresetMap = new HashMap<>(); private final Map fldPresetMap = new HashMap<>(); private final Map mthPresetMap = new HashMap<>(); - private final Map> varPresetMap = new HashMap<>(); @Nullable public static DeobfPresets build(RootNode root) { @@ -47,7 +43,7 @@ public class DeobfPresets { if (deobfMapPath == null) { return null; } - LOG.info("Deobfuscation map file set to: {}", deobfMapPath); + LOG.debug("Deobfuscation map file set to: {}", deobfMapPath); return new DeobfPresets(deobfMapPath); } @@ -106,11 +102,7 @@ public class DeobfPresets { mthPresetMap.put(origName, alias); break; case 'v': - String[] mthIDAndVarIndex = origName.split(VariableNode.VAR_SEPARATOR); - if (mthIDAndVarIndex.length == 2) { - Set nameList = varPresetMap.computeIfAbsent(mthIDAndVarIndex[0], k -> new HashSet<>()); - nameList.add(makeVarSecIndex(mthIDAndVarIndex[1], alias)); - } + // deprecated break; } } @@ -119,10 +111,6 @@ public class DeobfPresets { } } - public static String makeVarSecIndex(String indexes, String name) { - return indexes + VariableNode.VAR_SEPARATOR + name; - } - private static String[] splitAndTrim(String str) { String[] v = str.substring(2).split("="); for (int i = 0; i < v.length; i++) { @@ -145,15 +133,6 @@ public class DeobfPresets { for (Map.Entry mthEntry : mthPresetMap.entrySet()) { list.add(String.format("m %s = %s", mthEntry.getKey(), mthEntry.getValue())); } - for (Map.Entry> varEntry : varPresetMap.entrySet()) { - for (String val : varEntry.getValue()) { - String[] indexAndName = val.split(VariableNode.VAR_SEPARATOR); - if (indexAndName.length == 2) { - list.add(String.format("v %s%s%s = %s", - varEntry.getKey(), VariableNode.VAR_SEPARATOR, indexAndName[0], indexAndName[1])); - } - } - } Collections.sort(list); Files.write(deobfMapFile, list, MAP_FILE_CHARSET, StandardOpenOption.WRITE, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING); @@ -183,18 +162,10 @@ public class DeobfPresets { return mthPresetMap.get(mth.getRawFullId()); } - public Set getForVars(MethodInfo mth) { - if (varPresetMap.isEmpty()) { - return null; - } - return varPresetMap.get(mth.getRawFullId()); - } - public void clear() { clsPresetMap.clear(); fldPresetMap.clear(); mthPresetMap.clear(); - varPresetMap.clear(); } public Path getDeobfMapFile() { @@ -216,18 +187,4 @@ public class DeobfPresets { public Map getMthPresetMap() { return mthPresetMap; } - - public Map> getVarPresetMap() { - return varPresetMap; - } - - public void updateVariableName(VariableNode node, String name) { - String key = node.getRenameKey(); - key = key.substring(0, key.indexOf(VariableNode.VAR_SEPARATOR)); - String newIndex = makeVarSecIndex(node.makeVarIndex(), name); - String oldIndex = makeVarSecIndex(node.makeVarIndex(), node.getName()); - Set indexSet = varPresetMap.computeIfAbsent(key, k -> new HashSet<>()); - indexSet.remove(oldIndex); - indexSet.add(newIndex); - } } diff --git a/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java b/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java index 66e9e46e5..22edc6901 100644 --- a/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java +++ b/jadx-core/src/main/java/jadx/core/deobf/Deobfuscator.java @@ -2,7 +2,14 @@ package jadx.core.deobf; import java.nio.file.Files; import java.nio.file.Path; -import java.util.*; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.NavigableSet; +import java.util.Set; +import java.util.TreeSet; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; @@ -212,11 +219,6 @@ public class Deobfuscator { } private void renameMethod(MethodNode mth) { - MethodInfo mthInfo = mth.getMethodInfo(); - Set names = deobfPresets.getForVars(mthInfo); - if (names != null) { - mthInfo.setVarNameMap(names); - } String alias = getMethodAlias(mth); if (alias != null) { applyMethodAlias(mth, alias); @@ -256,8 +258,10 @@ public class Deobfuscator { /** * Gets package node for full package name * - * @param fullPkgName full package name - * @param create if {@code true} then will create all absent objects + * @param fullPkgName + * full package name + * @param create + * if {@code true} then will create all absent objects * @return package node object or {@code null} if no package found and create set to * {@code false} */ @@ -338,6 +342,21 @@ public class Deobfuscator { public String getPkgAlias(ClassNode cls) { ClassInfo classInfo = cls.getClassInfo(); + if (classInfo.hasAliasPkg()) { + // already renamed + PackageNode pkg = getPackageNode(classInfo.getPackage(), true); + // update all parts of package + String[] aliasParts = classInfo.getAliasPkg().split("\\."); + PackageNode subPkg = pkg; + for (int i = aliasParts.length - 1; i >= 0; i--) { + String aliasPart = aliasParts[i]; + if (!subPkg.getName().equals(aliasPart)) { + subPkg.setAlias(aliasPart); + } + subPkg = subPkg.getParentPackage(); + } + return pkg.getFullAlias(); + } PackageNode pkg; DeobfClsInfo deobfClsInfo = clsMap.get(classInfo); if (deobfClsInfo != null) { diff --git a/jadx-core/src/main/java/jadx/core/deobf/PackageNode.java b/jadx-core/src/main/java/jadx/core/deobf/PackageNode.java index ccd2662a1..82a847ca2 100644 --- a/jadx-core/src/main/java/jadx/core/deobf/PackageNode.java +++ b/jadx-core/src/main/java/jadx/core/deobf/PackageNode.java @@ -55,6 +55,7 @@ public class PackageNode { public void setAlias(String alias) { packageAlias = alias; + cachedPackageFullAlias = null; } public boolean hasAlias() { @@ -109,7 +110,8 @@ public class PackageNode { /** * Gets inner package node by name * - * @param name inner package name + * @param name + * inner package name * @return package node or {@code null} */ public PackageNode getInnerPackageByName(String name) { @@ -143,6 +145,9 @@ public class PackageNode { @Override public String toString() { - return packageAlias; + if (packageAlias != null) { + return packageName + "[alias:" + packageAlias + "]"; + } + return packageName; } } diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/ILineAttributeNode.java b/jadx-core/src/main/java/jadx/core/dex/attributes/ILineAttributeNode.java new file mode 100644 index 000000000..2df6770b6 --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/attributes/ILineAttributeNode.java @@ -0,0 +1,15 @@ +package jadx.core.dex.attributes; + +public interface ILineAttributeNode { + int getSourceLine(); + + void setSourceLine(int sourceLine); + + int getDecompiledLine(); + + void setDecompiledLine(int line); + + int getDefPosition(); + + void setDefPosition(int pos); +} diff --git a/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/LineAttrNode.java b/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/LineAttrNode.java index 5cc782e09..d7348a4b0 100644 --- a/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/LineAttrNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/attributes/nodes/LineAttrNode.java @@ -1,8 +1,9 @@ package jadx.core.dex.attributes.nodes; import jadx.core.dex.attributes.AttrNode; +import jadx.core.dex.attributes.ILineAttributeNode; -public abstract class LineAttrNode extends AttrNode { +public abstract class LineAttrNode extends AttrNode implements ILineAttributeNode { private int sourceLine; @@ -11,26 +12,32 @@ public abstract class LineAttrNode extends AttrNode { // the position exactly where a node declared at in decompiled java code. private int defPosition; + @Override public int getDefPosition() { return this.defPosition; } + @Override public void setDefPosition(int defPosition) { this.defPosition = defPosition; } + @Override public int getSourceLine() { return sourceLine; } + @Override public void setSourceLine(int sourceLine) { this.sourceLine = sourceLine; } + @Override public int getDecompiledLine() { return decompiledLine; } + @Override public void setDecompiledLine(int decompiledLine) { this.decompiledLine = decompiledLine; } diff --git a/jadx-core/src/main/java/jadx/core/dex/info/ClassInfo.java b/jadx-core/src/main/java/jadx/core/dex/info/ClassInfo.java index 53fe7b982..0a33e823e 100644 --- a/jadx-core/src/main/java/jadx/core/dex/info/ClassInfo.java +++ b/jadx-core/src/main/java/jadx/core/dex/info/ClassInfo.java @@ -116,6 +116,17 @@ public final class ClassInfo implements Comparable { return parentClass != null && parentClass.hasAlias(); } + public boolean hasAliasPkg() { + if (alias != null) { + return !getPackage().equals(getAliasPkg()); + } + return parentClass != null && parentClass.hasAliasPkg(); + } + + public void removeAlias() { + this.alias = null; + } + private void splitAndApplyNames(RootNode root, ArgType type, boolean canBeInner) { String fullObjectName = type.getObject(); String clsPkg; diff --git a/jadx-core/src/main/java/jadx/core/dex/info/FieldInfo.java b/jadx-core/src/main/java/jadx/core/dex/info/FieldInfo.java index 8e28ebb0d..2e3b45e9e 100644 --- a/jadx-core/src/main/java/jadx/core/dex/info/FieldInfo.java +++ b/jadx-core/src/main/java/jadx/core/dex/info/FieldInfo.java @@ -52,6 +52,10 @@ public final class FieldInfo { this.alias = alias; } + public void removeAlias() { + this.alias = name; + } + public boolean hasAlias() { return !Objects.equals(name, alias); } diff --git a/jadx-core/src/main/java/jadx/core/dex/info/MethodInfo.java b/jadx-core/src/main/java/jadx/core/dex/info/MethodInfo.java index f9320e81b..ff06caa24 100644 --- a/jadx-core/src/main/java/jadx/core/dex/info/MethodInfo.java +++ b/jadx-core/src/main/java/jadx/core/dex/info/MethodInfo.java @@ -1,10 +1,7 @@ package jadx.core.dex.info; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Objects; -import java.util.Set; import org.jetbrains.annotations.Nullable; @@ -13,7 +10,6 @@ import jadx.api.plugins.input.data.IMethodRef; import jadx.core.codegen.TypeGen; import jadx.core.dex.instructions.args.ArgType; import jadx.core.dex.nodes.RootNode; -import jadx.core.dex.nodes.VariableNode; import jadx.core.utils.Utils; public final class MethodInfo implements Comparable { @@ -27,7 +23,6 @@ public final class MethodInfo implements Comparable { private final int hash; private String alias; - private Map varNameMap; private MethodInfo(ClassInfo declClass, String name, List args, ArgType retType) { this.name = name; @@ -158,33 +153,14 @@ public final class MethodInfo implements Comparable { this.alias = alias; } + public void removeAlias() { + this.alias = name; + } + public boolean hasAlias() { return !name.equals(alias); } - public synchronized void setVarNameMap(Set names) { - if (varNameMap == null) { - varNameMap = new HashMap<>(); - } - for (String name : names) { - String[] indexesAndName = name.split(VariableNode.VAR_SEPARATOR); - if (indexesAndName.length == 2) { - varNameMap.put(indexesAndName[0], indexesAndName[1]); - } - } - } - - public String getVariableName(String indexes) { - if (varNameMap != null) { - return varNameMap.get(indexes); - } - return null; - } - - public boolean hasVarNameMap() { - return varNameMap != null && varNameMap.size() > 0; - } - public int calcHashCode() { return shortId.hashCode() + 31 * declClass.hashCode(); } diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/CodeVar.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/CodeVar.java index 6705eb0ac..a4411d292 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/CodeVar.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/CodeVar.java @@ -4,7 +4,9 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -public class CodeVar implements VisibleVar { +import jadx.api.data.annotations.VarRef; + +public class CodeVar { private String name; private ArgType type; // before type inference can be null and set only for immutable types private List ssaVars = Collections.emptyList(); @@ -13,17 +15,7 @@ public class CodeVar implements VisibleVar { private boolean isThis; private boolean isDeclared; - private int index = -1; - - @Override - public int getIndex() { - return index; - } - - @Override - public void setIndex(int index) { - this.index = index; - } + private VarRef cachedVarRef; // set and used at codegen stage public static CodeVar fromMthArg(RegisterArg mthArg, boolean linkRegister) { CodeVar var = new CodeVar(); @@ -38,17 +30,14 @@ public class CodeVar implements VisibleVar { return var; } - @Override public String getName() { return name; } - @Override public void setName(String name) { this.name = name; } - @Override public ArgType getType() { return type; } @@ -74,6 +63,13 @@ public class CodeVar implements VisibleVar { this.ssaVars = ssaVars; } + public SSAVar getAnySsaVar() { + if (ssaVars.isEmpty()) { + throw new IllegalStateException("CodeVar without SSA variables attached: " + this); + } + return ssaVars.get(0); + } + public boolean isFinal() { return isFinal; } @@ -98,6 +94,14 @@ public class CodeVar implements VisibleVar { isDeclared = declared; } + public VarRef getCachedVarRef() { + return cachedVarRef; + } + + public void setCachedVarRef(VarRef cachedVarRef) { + this.cachedVarRef = cachedVarRef; + } + /** * Merge flags with OR operator */ diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/NamedArg.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/NamedArg.java index aa35f8977..3b04e8de6 100644 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/NamedArg.java +++ b/jadx-core/src/main/java/jadx/core/dex/instructions/args/NamedArg.java @@ -2,28 +2,16 @@ package jadx.core.dex.instructions.args; import org.jetbrains.annotations.NotNull; -public final class NamedArg extends InsnArg implements Named, VisibleVar { +public final class NamedArg extends InsnArg implements Named { @NotNull private String name; - private int index = -1; - public NamedArg(@NotNull String name, @NotNull ArgType type) { this.name = name; this.type = type; } - @Override - public int getIndex() { - return index; - } - - @Override - public void setIndex(int index) { - this.index = index; - } - @NotNull public String getName() { return name; diff --git a/jadx-core/src/main/java/jadx/core/dex/instructions/args/VisibleVar.java b/jadx-core/src/main/java/jadx/core/dex/instructions/args/VisibleVar.java deleted file mode 100644 index 21eed0664..000000000 --- a/jadx-core/src/main/java/jadx/core/dex/instructions/args/VisibleVar.java +++ /dev/null @@ -1,14 +0,0 @@ -package jadx.core.dex.instructions.args; - -public interface VisibleVar { - int getIndex(); - - void setIndex(int index); - - String getName(); - - void setName(String name); - - ArgType getType(); - -} diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/ICodeDataUpdateListener.java b/jadx-core/src/main/java/jadx/core/dex/nodes/ICodeDataUpdateListener.java new file mode 100644 index 000000000..eb1458810 --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/ICodeDataUpdateListener.java @@ -0,0 +1,8 @@ +package jadx.core.dex.nodes; + +import jadx.api.data.ICodeData; + +public interface ICodeDataUpdateListener { + + void updated(ICodeData codeData); +} diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java index c6b3523fd..cf668a698 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/MethodNode.java @@ -15,7 +15,6 @@ import jadx.api.plugins.input.data.IDebugInfo; import jadx.api.plugins.input.data.IMethodData; import jadx.api.plugins.input.data.attributes.JadxAttrType; import jadx.api.plugins.input.data.attributes.types.ExceptionsAttr; -import jadx.core.codegen.NameGen; import jadx.core.dex.attributes.AFlag; import jadx.core.dex.attributes.nodes.LoopInfo; import jadx.core.dex.attributes.nodes.NotificationAttrNode; @@ -24,13 +23,9 @@ import jadx.core.dex.info.AccessInfo.AFType; import jadx.core.dex.info.MethodInfo; import jadx.core.dex.instructions.InsnDecoder; import jadx.core.dex.instructions.args.ArgType; -import jadx.core.dex.instructions.args.CodeVar; import jadx.core.dex.instructions.args.InsnArg; -import jadx.core.dex.instructions.args.NamedArg; import jadx.core.dex.instructions.args.RegisterArg; import jadx.core.dex.instructions.args.SSAVar; -import jadx.core.dex.instructions.args.VisibleVar; -import jadx.core.dex.nodes.VariableNode.VarKind; import jadx.core.dex.nodes.utils.TypeUtils; import jadx.core.dex.regions.Region; import jadx.core.dex.trycatch.ExceptionHandler; @@ -74,7 +69,6 @@ public class MethodNode extends NotificationAttrNode implements IMethodDetails, private Region region; private List useIn = Collections.emptyList(); - private List variables = new ArrayList<>(); public static MethodNode build(ClassNode classNode, IMethodData methodData) { MethodNode methodNode = new MethodNode(classNode, methodData); @@ -102,47 +96,6 @@ public class MethodNode extends NotificationAttrNode implements IMethodDetails, unload(); } - public List getVars() { - return new ArrayList<>(variables); - } - - public VariableNode getVariable(int index) { - if (index >= 0 && index < variables.size()) { - return variables.get(index); - } - return null; - } - - public VariableNode declareVar(VisibleVar var, NameGen nameGen, VarKind varKind) { - if (var instanceof CodeVar) { - if (((CodeVar) var).isThis()) { - return null; - } - } - VariableNode varNode; - int index = var.getIndex(); - if (index > -1) { - varNode = getVariable(var.getIndex()); - } else { - index = variables.size(); - var.setIndex(index); - String name = mthInfo.getVariableName(VariableNode.makeVarIndex(index, varKind)); - if (name != null) { - var.setName(name); // set name with user renamed previously. - } - if (var instanceof CodeVar) { // let NameGen record this name or gen an valid name. - name = nameGen.assignArg((CodeVar) var); - } else if (var instanceof NamedArg) { - name = nameGen.assignNamedArg((NamedArg) var); - } else { - throw new JadxRuntimeException("Unexpected var type: " + var); - } - varNode = new VariableNode(this, name, var.getType(), varKind, index); - this.variables.add(varNode); - } - return varNode; - } - @Override public void unload() { loaded = false; diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java index 8232dfd9d..59551af25 100644 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java +++ b/jadx-core/src/main/java/jadx/core/dex/nodes/RootNode.java @@ -18,6 +18,7 @@ import jadx.api.JadxArgs; import jadx.api.ResourceFile; import jadx.api.ResourceType; import jadx.api.ResourcesLoader; +import jadx.api.data.ICodeData; import jadx.api.plugins.input.data.IClassData; import jadx.api.plugins.input.data.ILoadResult; import jadx.core.Jadx; @@ -51,6 +52,7 @@ public class RootNode { private final JadxArgs args; private final List preDecompilePasses; private final List passes; + private final List codeDataUpdateListeners = new ArrayList<>(); private final ErrorsCounter errorsCounter = new ErrorsCounter(); private final StringUtils stringUtils; @@ -470,6 +472,15 @@ public class RootNode { return jadxArgs.getCodeWriterProvider().apply(jadxArgs); } + public void registerCodeDataUpdateListener(ICodeDataUpdateListener listener) { + this.codeDataUpdateListeners.add(listener); + } + + public void notifyCodeDataListeners() { + ICodeData codeData = args.getCodeData(); + codeDataUpdateListeners.forEach(l -> l.updated(codeData)); + } + public ClspGraph getClsp() { return clsp; } diff --git a/jadx-core/src/main/java/jadx/core/dex/nodes/VariableNode.java b/jadx-core/src/main/java/jadx/core/dex/nodes/VariableNode.java deleted file mode 100644 index 89d91c038..000000000 --- a/jadx-core/src/main/java/jadx/core/dex/nodes/VariableNode.java +++ /dev/null @@ -1,96 +0,0 @@ -package jadx.core.dex.nodes; - -import jadx.core.dex.attributes.nodes.LineAttrNode; -import jadx.core.dex.instructions.args.ArgType; -import jadx.core.utils.exceptions.JadxRuntimeException; - -public class VariableNode extends LineAttrNode { - public enum VarKind { - // note: better not change the order of these fields, - // they are also used for variable renaming - VAR, ARG, CATCH_ARG - } - - public static final String VAR_SEPARATOR = "--->>"; // do not contain '=' - private VarKind varKind = VarKind.VAR; - private ArgType type; - private String name; - private int index; - MethodNode mth; - - public VariableNode(MethodNode mth, String name, ArgType type, VarKind varKind, int index) { - this.mth = mth; - this.name = name; - this.type = type; - this.index = index; - this.varKind = varKind; - } - - public MethodNode getMethodNode() { - return mth; - } - - public ClassNode getClassNode() { - return mth.getParentClass(); - } - - public VarKind getVarKind() { - return this.varKind; - } - - public void setName(String name) { - this.name = name; - } - - public String getName() { - return name; - } - - public ArgType getType() { - return type; - } - - public int getIndex() { - return index; - } - - public String getRenameKey() { - return mth.getMethodInfo().getRawFullId() + VAR_SEPARATOR + makeVarIndex(index, varKind); - } - - public String makeVarIndex() { - return makeVarIndex(index, varKind); - } - - public static String makeVarIndex(int index, VarKind kind) { - return kindToStr(kind) + "@" + kind.ordinal() + "_" + index; - } - - private static String kindToStr(VarKind varKind) { - String kind; - switch (varKind) { - case VAR: - kind = "var"; - break; - case ARG: - kind = "param"; - break; - case CATCH_ARG: - kind = "catch"; - break; - default: - throw new JadxRuntimeException("Unexpected variable type " + varKind); - } - return kind; - } - - @Override - public int hashCode() { - return mth.hashCode() + 31 * getDefPosition() + 31 * makeVarIndex().hashCode(); - } - - @Override - public boolean equals(Object obj) { - return this == obj; - } -} diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/AttachCommentsVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/AttachCommentsVisitor.java index 6780d36a7..9240a0bd9 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/AttachCommentsVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/AttachCommentsVisitor.java @@ -1,25 +1,27 @@ package jadx.core.dex.visitors; -import java.util.ArrayList; import java.util.Collections; -import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import org.jetbrains.annotations.Nullable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import jadx.api.data.CodeRefType; import jadx.api.data.ICodeComment; import jadx.api.data.ICodeData; +import jadx.api.data.IJavaCodeRef; import jadx.api.data.IJavaNodeRef; import jadx.core.dex.attributes.AType; import jadx.core.dex.attributes.IAttributeNode; -import jadx.core.dex.instructions.args.RegisterArg; import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.FieldNode; import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; +import jadx.core.dex.nodes.RootNode; +import jadx.core.utils.exceptions.JadxException; import jadx.core.utils.exceptions.JadxRuntimeException; @JadxVisitor( @@ -33,7 +35,13 @@ public class AttachCommentsVisitor extends AbstractVisitor { private static final Logger LOG = LoggerFactory.getLogger(AttachCommentsVisitor.class); - private final CommentsData cachedCommentsData = new CommentsData(); + private Map> clsCommentsMap; + + @Override + public void init(RootNode root) throws JadxException { + updateCommentsData(root.getArgs().getCodeData()); + root.registerCodeDataUpdateListener(this::updateCommentsData); + } @Override public boolean visit(ClassNode cls) { @@ -67,14 +75,11 @@ public class AttachCommentsVisitor extends AbstractVisitor { if (methodNode == null) { LOG.warn("Method reference not found: {}", nodeRef); } else { - int offset = comment.getOffset(); - if (offset < 0) { + IJavaCodeRef codeRef = comment.getCodeRef(); + if (codeRef == null) { addComment(methodNode, comment.getComment()); - } else if (comment.getAttachType() != null) { - processCustomAttach(methodNode, comment); } else { - InsnNode insn = getInsnByOffset(methodNode, offset); - addComment(insn, comment.getComment()); + processCustomAttach(methodNode, codeRef, comment); } } break; @@ -91,22 +96,14 @@ public class AttachCommentsVisitor extends AbstractVisitor { } } - private static void processCustomAttach(MethodNode mth, ICodeComment comment) { - ICodeComment.AttachType attachType = comment.getAttachType(); - if (attachType == null) { - return; - } + private static void processCustomAttach(MethodNode mth, IJavaCodeRef codeRef, ICodeComment comment) { + CodeRefType attachType = codeRef.getAttachType(); switch (attachType) { - case VAR_DECLARE: - InsnNode insn = getInsnByOffset(mth, comment.getOffset()); - if (insn != null) { - RegisterArg result = insn.getResult(); - if (result != null) { - result.addAttr(AType.CODE_COMMENTS, comment.getComment()); - } - } + case INSN: { + InsnNode insn = getInsnByOffset(mth, codeRef.getIndex()); + addComment(insn, comment.getComment()); break; - + } default: throw new JadxRuntimeException("Unexpected attach type: " + attachType); } @@ -120,37 +117,23 @@ public class AttachCommentsVisitor extends AbstractVisitor { node.addAttr(AType.CODE_COMMENTS, comment); } - private static final class CommentsData { - long updateId; - Map> clsCommentsMap; - } - private List getCommentsData(ClassNode cls) { - ICodeData additionalData = cls.root().getArgs().getCodeData(); - if (additionalData == null || additionalData.getComments().isEmpty()) { + if (clsCommentsMap == null) { return Collections.emptyList(); } - synchronized (cachedCommentsData) { - CommentsData commentsData = this.cachedCommentsData; - if (commentsData.updateId != additionalData.getUpdateId()) { - updateCommentsData(additionalData, commentsData); - } - List clsComments = commentsData.clsCommentsMap.get(cls.getClassInfo().getFullName()); - if (clsComments == null) { - return Collections.emptyList(); - } - return clsComments; + List clsComments = clsCommentsMap.get(cls.getClassInfo().getRawName()); + if (clsComments == null) { + return Collections.emptyList(); } + return clsComments; } - private static void updateCommentsData(ICodeData data, CommentsData commentsData) { - Map> map = new HashMap<>(); - for (ICodeComment comment : data.getComments()) { - String declClsId = comment.getNodeRef().getDeclaringClass(); - List comments = map.computeIfAbsent(declClsId, s -> new ArrayList<>()); - comments.add(comment); + private void updateCommentsData(@Nullable ICodeData data) { + if (data == null) { + this.clsCommentsMap = Collections.emptyMap(); + } else { + this.clsCommentsMap = data.getComments().stream() + .collect(Collectors.groupingBy(c -> c.getNodeRef().getDeclaringClass())); } - commentsData.clsCommentsMap = map; - commentsData.updateId = data.getUpdateId(); } } diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/OverrideMethodVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/OverrideMethodVisitor.java index db257cdb8..8c8ac76f2 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/OverrideMethodVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/OverrideMethodVisitor.java @@ -24,6 +24,7 @@ import jadx.core.dex.nodes.ClassNode; import jadx.core.dex.nodes.IMethodDetails; import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.nodes.RootNode; +import jadx.core.dex.visitors.rename.RenameVisitor; import jadx.core.dex.visitors.typeinference.TypeCompare; import jadx.core.dex.visitors.typeinference.TypeCompareEnum; import jadx.core.dex.visitors.typeinference.TypeInferenceVisitor; diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/rename/CodeRenameVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/rename/CodeRenameVisitor.java new file mode 100644 index 000000000..1fe6b1d3e --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/rename/CodeRenameVisitor.java @@ -0,0 +1,125 @@ +package jadx.core.dex.visitors.rename; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.jetbrains.annotations.Nullable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import jadx.api.data.ICodeData; +import jadx.api.data.ICodeRename; +import jadx.api.data.IJavaCodeRef; +import jadx.api.data.IJavaNodeRef; +import jadx.core.dex.instructions.args.RegisterArg; +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; + +@JadxVisitor( + name = "ApplyCodeRename", + desc = "Rename variables and other entities in methods", + runAfter = { + InitCodeVariables.class, + DebugInfoApplyVisitor.class + } +) +public class CodeRenameVisitor extends AbstractVisitor { + + private static final Logger LOG = LoggerFactory.getLogger(CodeRenameVisitor.class); + + private Map> clsRenamesMap; + + @Override + public void init(RootNode root) throws JadxException { + updateRenamesMap(root.getArgs().getCodeData()); + root.registerCodeDataUpdateListener(this::updateRenamesMap); + } + + @Override + public boolean visit(ClassNode cls) { + List renames = getRenames(cls); + if (!renames.isEmpty()) { + applyRenames(cls, renames); + } + cls.getInnerClasses().forEach(this::visit); + return false; + } + + private static void applyRenames(ClassNode cls, List renames) { + for (ICodeRename rename : renames) { + IJavaNodeRef nodeRef = rename.getNodeRef(); + if (nodeRef.getType() == IJavaNodeRef.RefType.METHOD) { + MethodNode methodNode = cls.searchMethodByShortId(nodeRef.getShortId()); + if (methodNode == null) { + LOG.warn("Method reference not found: {}", nodeRef); + } else { + IJavaCodeRef codeRef = rename.getCodeRef(); + if (codeRef != null) { + processRename(methodNode, codeRef, rename); + } + } + } + } + } + + private static void processRename(MethodNode mth, IJavaCodeRef codeRef, ICodeRename rename) { + switch (codeRef.getAttachType()) { + case MTH_ARG: { + List argRegs = mth.getArgRegs(); + int argNum = codeRef.getIndex(); + if (argNum < argRegs.size()) { + argRegs.get(argNum).getSVar().getCodeVar().setName(rename.getNewName()); + } else { + LOG.warn("Incorrect method arg ref {}, should be less than {}", argNum, argRegs.size()); + } + break; + } + case VAR: { + int regNum = codeRef.getIndex() >> 16; + int ssaVer = codeRef.getIndex() & 0xFFFF; + for (SSAVar ssaVar : mth.getSVars()) { + if (ssaVar.getRegNum() == regNum && ssaVar.getVersion() == ssaVer) { + ssaVar.getCodeVar().setName(rename.getNewName()); + return; + } + } + LOG.warn("Can't find variable ref by {}_{}", regNum, ssaVer); + break; + } + + default: + LOG.warn("Rename code ref type {} not yet supported", codeRef.getAttachType()); + break; + } + } + + private List getRenames(ClassNode cls) { + if (clsRenamesMap == null) { + return Collections.emptyList(); + } + List clsComments = clsRenamesMap.get(cls.getClassInfo().getFullName()); + if (clsComments == null) { + return Collections.emptyList(); + } + return clsComments; + } + + private void updateRenamesMap(@Nullable ICodeData data) { + if (data == null) { + this.clsRenamesMap = Collections.emptyMap(); + } else { + this.clsRenamesMap = data.getRenames().stream() + .filter(r -> r.getCodeRef() != null) + .collect(Collectors.groupingBy(r -> r.getNodeRef().getDeclaringClass())); + } + } +} diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/RenameVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/rename/RenameVisitor.java similarity index 98% rename from jadx-core/src/main/java/jadx/core/dex/visitors/RenameVisitor.java rename to jadx-core/src/main/java/jadx/core/dex/visitors/rename/RenameVisitor.java index cba2f5802..8229c5c82 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/RenameVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/rename/RenameVisitor.java @@ -1,4 +1,4 @@ -package jadx.core.dex.visitors; +package jadx.core.dex.visitors.rename; import java.io.File; import java.util.ArrayList; @@ -22,6 +22,7 @@ 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; public class RenameVisitor extends AbstractVisitor { @@ -37,11 +38,13 @@ public class RenameVisitor extends AbstractVisitor { private void process(RootNode root) { Deobfuscator deobfuscator = new Deobfuscator(root); JadxArgs args = root.getArgs(); + if (args.isDeobfuscationOn()) { deobfuscator.execute(); } checkClasses(deobfuscator, root, args); + UserRenames.applyForNodes(root); if (args.isDeobfuscationOn()) { deobfuscator.savePresets(); diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/rename/UserRenames.java b/jadx-core/src/main/java/jadx/core/dex/visitors/rename/UserRenames.java new file mode 100644 index 000000000..bce60be64 --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/rename/UserRenames.java @@ -0,0 +1,132 @@ +package jadx.core.dex.visitors.rename; + +import java.util.List; +import java.util.stream.Collectors; + +import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import jadx.api.data.ICodeData; +import jadx.api.data.ICodeRename; +import jadx.api.data.IJavaCodeRef; +import jadx.api.data.IJavaNodeRef; +import jadx.core.dex.attributes.AType; +import jadx.core.dex.attributes.nodes.MethodOverrideAttr; +import jadx.core.dex.info.ClassInfo; +import jadx.core.dex.info.InfoStorage; +import jadx.core.dex.instructions.args.ArgType; +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 UserRenames { + private static final Logger LOG = LoggerFactory.getLogger(UserRenames.class); + + public static void applyForNodes(RootNode root) { + ICodeData codeData = root.getArgs().getCodeData(); + if (codeData == null || codeData.getRenames().isEmpty()) { + return; + } + InfoStorage infoStorage = root.getInfoStorage(); + codeData.getRenames().stream() + .filter(r -> r.getCodeRef() == null && r.getNodeRef().getType() != IJavaNodeRef.RefType.PKG) + .collect(Collectors.groupingBy(r -> r.getNodeRef().getDeclaringClass())) + .forEach((clsRawName, renames) -> { + ClassInfo clsInfo = infoStorage.getCls(ArgType.object(clsRawName)); + if (clsInfo != null) { + ClassNode cls = root.resolveClass(clsInfo); + if (cls != null) { + for (ICodeRename rename : renames) { + applyRename(cls, rename); + } + return; + } + } + LOG.warn("Class info with reference '{}' not found", clsRawName); + }); + applyPkgRenames(root, codeData.getRenames()); + } + + private static void applyRename(ClassNode cls, ICodeRename rename) { + IJavaNodeRef nodeRef = rename.getNodeRef(); + switch (nodeRef.getType()) { + case CLASS: + cls.getClassInfo().changeShortName(rename.getNewName()); + break; + + case FIELD: + FieldNode fieldNode = cls.searchFieldByShortId(nodeRef.getShortId()); + if (fieldNode == null) { + LOG.warn("Field reference not found: {}", nodeRef); + } else { + fieldNode.getFieldInfo().setAlias(rename.getNewName()); + } + break; + + case METHOD: + MethodNode mth = cls.searchMethodByShortId(nodeRef.getShortId()); + if (mth == null) { + LOG.warn("Method reference not found: {}", nodeRef); + } else { + IJavaCodeRef codeRef = rename.getCodeRef(); + if (codeRef == null) { + applyMethodRename(mth, rename); + } + } + break; + } + } + + private static void applyMethodRename(MethodNode mth, ICodeRename rename) { + MethodOverrideAttr overrideAttr = mth.get(AType.METHOD_OVERRIDE); + if (overrideAttr != null) { + for (MethodNode relatedMth : overrideAttr.getRelatedMthNodes()) { + renameMethod(relatedMth, rename); + } + } else { + renameMethod(mth, rename); + } + } + + private static void renameMethod(MethodNode mth, ICodeRename rename) { + mth.getMethodInfo().setAlias(rename.getNewName()); + } + + // TODO: Very inefficient!!! Add PackageInfo class to build package hierarchy + private static void applyPkgRenames(RootNode root, List renames) { + List classes = root.getClasses(false); + renames.stream() + .filter(r -> r.getNodeRef().getType() == IJavaNodeRef.RefType.PKG) + .forEach(pkgRename -> { + String pkgFullName = pkgRename.getNodeRef().getDeclaringClass(); + String pkgFullNameDot = pkgFullName + "."; + for (ClassNode cls : classes) { + ClassInfo clsInfo = cls.getClassInfo(); + String pkg = clsInfo.getPackage(); + if (pkg.equals(pkgFullName)) { + clsInfo.changePkg(cutLastPkgPart(clsInfo.getAliasPkg()) + '.' + pkgRename.getNewName()); + } else if (pkg.startsWith(pkgFullNameDot)) { + clsInfo.changePkg(rebuildPkgMiddle(clsInfo.getAliasPkg(), pkgFullName, pkgRename.getNewName())); + } + } + }); + } + + @NotNull + private static String cutLastPkgPart(String pkgFullName) { + int lastDotIndex = pkgFullName.lastIndexOf('.'); + if (lastDotIndex == -1) { + return pkgFullName; + } + return pkgFullName.substring(0, lastDotIndex); + } + + private static String rebuildPkgMiddle(String aliasPkg, String renameOriginPkg, String newName) { + String[] aliasParts = aliasPkg.split("\\."); + String[] renameParts = renameOriginPkg.split("\\."); + aliasParts[renameParts.length - 1] = newName; + return String.join(".", aliasParts); + } +} diff --git a/jadx-core/src/main/java/jadx/core/dex/visitors/usage/UsageInfoVisitor.java b/jadx-core/src/main/java/jadx/core/dex/visitors/usage/UsageInfoVisitor.java index 0b1bd81d6..a868b2f7b 100644 --- a/jadx-core/src/main/java/jadx/core/dex/visitors/usage/UsageInfoVisitor.java +++ b/jadx-core/src/main/java/jadx/core/dex/visitors/usage/UsageInfoVisitor.java @@ -17,7 +17,7 @@ import jadx.core.dex.nodes.RootNode; import jadx.core.dex.visitors.AbstractVisitor; import jadx.core.dex.visitors.JadxVisitor; import jadx.core.dex.visitors.OverrideMethodVisitor; -import jadx.core.dex.visitors.RenameVisitor; +import jadx.core.dex.visitors.rename.RenameVisitor; import jadx.core.utils.input.InsnDataUtils; @JadxVisitor( diff --git a/jadx-core/src/main/java/jadx/core/utils/DebugChecks.java b/jadx-core/src/main/java/jadx/core/utils/DebugChecks.java index 8da3d0e0e..b42b8758d 100644 --- a/jadx-core/src/main/java/jadx/core/utils/DebugChecks.java +++ b/jadx-core/src/main/java/jadx/core/utils/DebugChecks.java @@ -19,7 +19,7 @@ import jadx.core.dex.nodes.InsnNode; import jadx.core.dex.nodes.MethodNode; import jadx.core.dex.visitors.IDexTreeVisitor; import jadx.core.dex.visitors.PrepareForCodeGen; -import jadx.core.dex.visitors.RenameVisitor; +import jadx.core.dex.visitors.rename.RenameVisitor; import jadx.core.utils.exceptions.JadxRuntimeException; /** diff --git a/jadx-core/src/test/java/jadx/tests/api/utils/assertj/JadxCodeInfoAssertions.java b/jadx-core/src/test/java/jadx/tests/api/utils/assertj/JadxCodeInfoAssertions.java index 465d1e859..1fd5509d6 100644 --- a/jadx-core/src/test/java/jadx/tests/api/utils/assertj/JadxCodeInfoAssertions.java +++ b/jadx-core/src/test/java/jadx/tests/api/utils/assertj/JadxCodeInfoAssertions.java @@ -23,7 +23,7 @@ public class JadxCodeInfoAssertions extends AbstractObjectAssert o instanceof ICodeRawOffset) + .filter(ICodeRawOffset.class::isInstance) .collect(Collectors.groupingBy(o -> ((ICodeRawOffset) o).getOffset(), Collectors.toList())) .values().stream() .filter(list -> list.size() > 1) diff --git a/jadx-core/src/test/java/jadx/tests/integration/others/TestCodeComments.java b/jadx-core/src/test/java/jadx/tests/integration/others/TestCodeComments.java index d6bc4f344..25db9a820 100644 --- a/jadx-core/src/test/java/jadx/tests/integration/others/TestCodeComments.java +++ b/jadx-core/src/test/java/jadx/tests/integration/others/TestCodeComments.java @@ -6,9 +6,11 @@ import java.util.Collections; import org.junit.jupiter.api.Test; import jadx.api.data.ICodeComment; +import jadx.api.data.IJavaCodeRef; import jadx.api.data.IJavaNodeRef.RefType; import jadx.api.data.impl.JadxCodeComment; import jadx.api.data.impl.JadxCodeData; +import jadx.api.data.impl.JadxCodeRef; import jadx.api.data.impl.JadxNodeRef; import jadx.core.dex.nodes.ClassNode; import jadx.tests.api.IntegrationTest; @@ -17,6 +19,7 @@ import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat; public class TestCodeComments extends IntegrationTest { + @SuppressWarnings("FieldCanBeLocal") public static class TestCls { private int intField = 5; @@ -32,15 +35,14 @@ public class TestCodeComments extends IntegrationTest { @Test public void test() { - int insnOffset = isJavaInput() ? 13 : 11; - String baseClsId = TestCls.class.getName(); ICodeComment clsComment = new JadxCodeComment(JadxNodeRef.forCls(baseClsId), "class comment"); - ICodeComment innerClsComment = new JadxCodeComment(JadxNodeRef.forCls(baseClsId + ".A"), "inner class comment"); + ICodeComment innerClsComment = new JadxCodeComment(JadxNodeRef.forCls(baseClsId + "$A"), "inner class comment"); ICodeComment fldComment = new JadxCodeComment(new JadxNodeRef(RefType.FIELD, baseClsId, "intField:I"), "field comment"); JadxNodeRef mthRef = new JadxNodeRef(RefType.METHOD, baseClsId, "test()I"); ICodeComment mthComment = new JadxCodeComment(mthRef, "method comment"); - ICodeComment insnComment = new JadxCodeComment(mthRef, "insn comment", insnOffset); + IJavaCodeRef insnRef = JadxCodeRef.forInsn(isJavaInput() ? 13 : 11); + ICodeComment insnComment = new JadxCodeComment(mthRef, insnRef, "insn comment"); JadxCodeData codeData = new JadxCodeData(); getArgs().setCodeData(codeData); @@ -62,8 +64,9 @@ public class TestCodeComments extends IntegrationTest { .reloadCode(this) .isEqualTo(code); - ICodeComment updInsnComment = new JadxCodeComment(mthRef, "updated insn comment", insnOffset); + ICodeComment updInsnComment = new JadxCodeComment(mthRef, insnRef, "updated insn comment"); codeData.setComments(Collections.singletonList(updInsnComment)); + jadxDecompiler.reloadCodeData(); assertThat(cls) .reloadCode(this) .containsOne("System.out.println(\"comment\"); // updated insn comment") diff --git a/jadx-core/src/test/java/jadx/tests/integration/others/TestCodeComments2.java b/jadx-core/src/test/java/jadx/tests/integration/others/TestCodeComments2.java index cecd3c1b4..8fecc0d3b 100644 --- a/jadx-core/src/test/java/jadx/tests/integration/others/TestCodeComments2.java +++ b/jadx-core/src/test/java/jadx/tests/integration/others/TestCodeComments2.java @@ -5,9 +5,11 @@ import java.util.Arrays; import org.junit.jupiter.api.Test; import jadx.api.data.ICodeComment; +import jadx.api.data.IJavaCodeRef; import jadx.api.data.IJavaNodeRef.RefType; import jadx.api.data.impl.JadxCodeComment; import jadx.api.data.impl.JadxCodeData; +import jadx.api.data.impl.JadxCodeRef; import jadx.api.data.impl.JadxNodeRef; import jadx.tests.api.IntegrationTest; @@ -31,8 +33,10 @@ public class TestCodeComments2 extends IntegrationTest { String baseClsId = TestCls.class.getName(); JadxNodeRef mthRef = new JadxNodeRef(RefType.METHOD, baseClsId, "test(Z)I"); - ICodeComment insnComment = new JadxCodeComment(mthRef, "return comment", isJavaInput() ? 13 : 10); - ICodeComment insnComment2 = new JadxCodeComment(mthRef, "another return comment", isJavaInput() ? 15 : 11); + IJavaCodeRef insnRef = JadxCodeRef.forInsn(isJavaInput() ? 13 : 10); + ICodeComment insnComment = new JadxCodeComment(mthRef, insnRef, "return comment"); + IJavaCodeRef insnRef2 = JadxCodeRef.forInsn(isJavaInput() ? 15 : 11); + ICodeComment insnComment2 = new JadxCodeComment(mthRef, insnRef2, "another return comment"); JadxCodeData codeData = new JadxCodeData(); codeData.setComments(Arrays.asList(insnComment, insnComment2)); diff --git a/jadx-core/src/test/java/jadx/tests/integration/others/TestCodeComments2a.java b/jadx-core/src/test/java/jadx/tests/integration/others/TestCodeComments2a.java index af20d8074..60d6ddbc5 100644 --- a/jadx-core/src/test/java/jadx/tests/integration/others/TestCodeComments2a.java +++ b/jadx-core/src/test/java/jadx/tests/integration/others/TestCodeComments2a.java @@ -6,9 +6,11 @@ import java.util.Random; import org.junit.jupiter.api.Test; import jadx.api.data.ICodeComment; +import jadx.api.data.IJavaCodeRef; import jadx.api.data.IJavaNodeRef.RefType; import jadx.api.data.impl.JadxCodeComment; import jadx.api.data.impl.JadxCodeData; +import jadx.api.data.impl.JadxCodeRef; import jadx.api.data.impl.JadxNodeRef; import jadx.tests.api.IntegrationTest; @@ -16,6 +18,7 @@ import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat; public class TestCodeComments2a extends IntegrationTest { + @SuppressWarnings("unused") public static class TestCls { private int f; @@ -34,8 +37,10 @@ public class TestCodeComments2a extends IntegrationTest { String baseClsId = TestCls.class.getName(); JadxNodeRef mthRef = new JadxNodeRef(RefType.METHOD, baseClsId, "test(Z)I"); - ICodeComment insnComment = new JadxCodeComment(mthRef, "return comment", isJavaInput() ? 22 : 18); - ICodeComment insnComment2 = new JadxCodeComment(mthRef, "another return comment", isJavaInput() ? 27 : 19); + IJavaCodeRef insnRef = JadxCodeRef.forInsn(isJavaInput() ? 22 : 18); + ICodeComment insnComment = new JadxCodeComment(mthRef, insnRef, "return comment"); + IJavaCodeRef insnRef2 = JadxCodeRef.forInsn(isJavaInput() ? 27 : 19); + ICodeComment insnComment2 = new JadxCodeComment(mthRef, insnRef2, "another return comment"); JadxCodeData codeData = new JadxCodeData(); codeData.setComments(Arrays.asList(insnComment, insnComment2)); diff --git a/jadx-core/src/test/java/jadx/tests/integration/others/TestCodeCommentsMultiline.java b/jadx-core/src/test/java/jadx/tests/integration/others/TestCodeCommentsMultiline.java index 2b1bfa4ee..9404d321a 100644 --- a/jadx-core/src/test/java/jadx/tests/integration/others/TestCodeCommentsMultiline.java +++ b/jadx-core/src/test/java/jadx/tests/integration/others/TestCodeCommentsMultiline.java @@ -5,9 +5,11 @@ import java.util.Collections; import org.junit.jupiter.api.Test; import jadx.api.data.ICodeComment; +import jadx.api.data.IJavaCodeRef; import jadx.api.data.IJavaNodeRef.RefType; import jadx.api.data.impl.JadxCodeComment; import jadx.api.data.impl.JadxCodeData; +import jadx.api.data.impl.JadxCodeRef; import jadx.api.data.impl.JadxNodeRef; import jadx.tests.api.IntegrationTest; @@ -31,7 +33,8 @@ public class TestCodeCommentsMultiline extends IntegrationTest { String baseClsId = TestCls.class.getName(); JadxNodeRef mthRef = new JadxNodeRef(RefType.METHOD, baseClsId, "test(Z)I"); - ICodeComment insnComment = new JadxCodeComment(mthRef, "multi\nline\ncomment", isJavaInput() ? 15 : 11); + IJavaCodeRef insnRef = JadxCodeRef.forInsn(isJavaInput() ? 15 : 11); + ICodeComment insnComment = new JadxCodeComment(mthRef, insnRef, "multi\nline\ncomment"); JadxCodeData codeData = new JadxCodeData(); codeData.setComments(Collections.singletonList(insnComment)); diff --git a/jadx-core/src/test/java/jadx/tests/integration/others/TestCodeCommentsOverride.java b/jadx-core/src/test/java/jadx/tests/integration/others/TestCodeCommentsOverride.java index 0e45c00a1..7eec0fa80 100644 --- a/jadx-core/src/test/java/jadx/tests/integration/others/TestCodeCommentsOverride.java +++ b/jadx-core/src/test/java/jadx/tests/integration/others/TestCodeCommentsOverride.java @@ -32,10 +32,10 @@ public class TestCodeCommentsOverride extends IntegrationTest { @Test public void test() { String baseClsId = TestCls.class.getName(); - JadxNodeRef iMthRef = new JadxNodeRef(RefType.METHOD, baseClsId + ".I", "mth()V"); + JadxNodeRef iMthRef = new JadxNodeRef(RefType.METHOD, baseClsId + "$I", "mth()V"); ICodeComment iMthComment = new JadxCodeComment(iMthRef, "interface mth comment"); - JadxNodeRef mthRef = new JadxNodeRef(RefType.METHOD, baseClsId + ".A", "mth()V"); + JadxNodeRef mthRef = new JadxNodeRef(RefType.METHOD, baseClsId + "$A", "mth()V"); ICodeComment mthComment = new JadxCodeComment(mthRef, "mth comment"); JadxCodeData codeData = new JadxCodeData(); diff --git a/jadx-core/src/test/java/jadx/tests/integration/rename/TestUserRenames.java b/jadx-core/src/test/java/jadx/tests/integration/rename/TestUserRenames.java new file mode 100644 index 000000000..1b22e51cf --- /dev/null +++ b/jadx-core/src/test/java/jadx/tests/integration/rename/TestUserRenames.java @@ -0,0 +1,91 @@ +package jadx.tests.integration.rename; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.junit.jupiter.api.Test; + +import jadx.api.data.CodeRefType; +import jadx.api.data.ICodeRename; +import jadx.api.data.IJavaCodeRef; +import jadx.api.data.IJavaNodeRef.RefType; +import jadx.api.data.impl.JadxCodeData; +import jadx.api.data.impl.JadxCodeRef; +import jadx.api.data.impl.JadxCodeRename; +import jadx.api.data.impl.JadxNodeRef; +import jadx.core.dex.nodes.ClassNode; +import jadx.tests.api.IntegrationTest; + +import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat; + +public class TestUserRenames extends IntegrationTest { + + @SuppressWarnings({ "FieldCanBeLocal", "FieldMayBeFinal" }) + public static class TestCls { + private int intField = 5; + + public static class A { + } + + public int test(int x) { + int y = x + "test".length(); + System.out.println(y); + int z = y + 1; + System.out.println(z); + return z; + } + } + + @Test + public void test() { + getArgs().setDeobfuscationOn(false); + + List renames = new ArrayList<>(); + String baseClsId = TestCls.class.getName(); + renames.add(new JadxCodeRename(JadxNodeRef.forPkg("jadx.tests"), "renamedPkgTests")); + renames.add(new JadxCodeRename(JadxNodeRef.forPkg("jadx.tests.integration.rename"), "renamedPkgRename")); + renames.add(new JadxCodeRename(JadxNodeRef.forCls(baseClsId), "RenamedTestCls")); + renames.add(new JadxCodeRename(JadxNodeRef.forCls(baseClsId + "$A"), "RenamedInnerCls")); + renames.add(new JadxCodeRename(new JadxNodeRef(RefType.FIELD, baseClsId, "intField:I"), "renamedField")); + JadxNodeRef mthRef = new JadxNodeRef(RefType.METHOD, baseClsId, "test(I)I"); + renames.add(new JadxCodeRename(mthRef, "renamedTestMth")); + renames.add(new JadxCodeRename(mthRef, new JadxCodeRef(CodeRefType.MTH_ARG, 0), "renamedX")); + JadxCodeRef varDeclareRef = isJavaInput() ? JadxCodeRef.forVar(0, 1) : JadxCodeRef.forVar(0, 0); + renames.add(new JadxCodeRename(mthRef, varDeclareRef, "renamedY")); + IJavaCodeRef varUseRef = isJavaInput() ? JadxCodeRef.forVar(0, 4) : JadxCodeRef.forVar(1, 0); + renames.add(new JadxCodeRename(mthRef, varUseRef, "renamedZ")); + + JadxCodeData codeData = new JadxCodeData(); + codeData.setRenames(renames); + getArgs().setCodeData(codeData); + + ClassNode cls = getClassNode(TestCls.class); + assertThat(cls) + .decompile() + .checkCodeOffsets() + .code() + .containsOne("package jadx.renamedPkgTests.integration.renamedPkgRename;") + .containsOne("public class RenamedTestCls {") + .containsOne("private int renamedField") + .containsOne("public static class RenamedInnerCls {") + .containsOne("public int renamedTestMth(int renamedX) {") + .containsOne("int renamedY = renamedX + \"test\".length();") + .containsOne("int renamedZ = renamedY + 1;") + .containsOne("return renamedZ;"); + + String code = cls.getCode().getCodeStr(); + assertThat(cls) + .reloadCode(this) + .isEqualTo(code); + + ICodeRename updVarRename = new JadxCodeRename(mthRef, varUseRef, "anotherZ"); + codeData.setRenames(Collections.singletonList(updVarRename)); + jadxDecompiler.reloadCodeData(); + assertThat(cls) + .reloadCode(this) + .containsOne("int anotherZ = y + 1;") + .doesNotContain("int z") + .doesNotContain("int renamedZ"); + } +} diff --git a/jadx-gui/src/main/java/jadx/gui/settings/JadxProject.java b/jadx-gui/src/main/java/jadx/gui/settings/JadxProject.java index c91b571bc..6598f7f57 100644 --- a/jadx-gui/src/main/java/jadx/gui/settings/JadxProject.java +++ b/jadx-gui/src/main/java/jadx/gui/settings/JadxProject.java @@ -16,9 +16,13 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; import jadx.api.data.ICodeComment; +import jadx.api.data.ICodeRename; +import jadx.api.data.IJavaCodeRef; import jadx.api.data.IJavaNodeRef; import jadx.api.data.impl.JadxCodeComment; import jadx.api.data.impl.JadxCodeData; +import jadx.api.data.impl.JadxCodeRef; +import jadx.api.data.impl.JadxCodeRename; import jadx.api.data.impl.JadxNodeRef; import jadx.core.utils.GsonUtils; import jadx.core.utils.exceptions.JadxRuntimeException; @@ -34,7 +38,9 @@ public class JadxProject { private static final Gson GSON = new GsonBuilder() .registerTypeHierarchyAdapter(Path.class, PathTypeAdapter.singleton()) .registerTypeAdapter(ICodeComment.class, GsonUtils.interfaceReplace(JadxCodeComment.class)) + .registerTypeAdapter(ICodeRename.class, GsonUtils.interfaceReplace(JadxCodeRename.class)) .registerTypeAdapter(IJavaNodeRef.class, GsonUtils.interfaceReplace(JadxNodeRef.class)) + .registerTypeAdapter(IJavaCodeRef.class, GsonUtils.interfaceReplace(JadxCodeRef.class)) .setPrettyPrinting() .create(); diff --git a/jadx-gui/src/main/java/jadx/gui/treemodel/JVariable.java b/jadx-gui/src/main/java/jadx/gui/treemodel/JVariable.java index e70d03a12..567d7ae85 100644 --- a/jadx-gui/src/main/java/jadx/gui/treemodel/JVariable.java +++ b/jadx-gui/src/main/java/jadx/gui/treemodel/JVariable.java @@ -8,21 +8,16 @@ import jadx.api.JavaVariable; public class JVariable extends JNode { private static final long serialVersionUID = -3002100457834453783L; - JClass cls; - JavaVariable var; + private final JMethod jMth; + private final JavaVariable var; - public JVariable(JavaVariable var, JClass cls) { - this.cls = cls; + public JVariable(JMethod jMth, JavaVariable var) { + this.jMth = jMth; this.var = var; } public JavaVariable getJavaVarNode() { - return (JavaVariable) getJavaNode(); - } - - @Override - public JClass getRootClass() { - return cls; + return var; } @Override @@ -30,9 +25,14 @@ public class JVariable extends JNode { return var; } + @Override + public JClass getRootClass() { + return jMth.getRootClass(); + } + @Override public JClass getJParent() { - return cls; + return jMth.getJParent(); } @Override @@ -45,6 +45,11 @@ public class JVariable extends JNode { return var.getName(); } + @Override + public String makeLongString() { + return var.getFullName(); + } + @Override public boolean canRename() { return true; diff --git a/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java b/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java index d8bacf945..9140f2d58 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java @@ -609,15 +609,6 @@ public class MainWindow extends JFrame { } private void saveAll(boolean export) { - JadxArgs decompilerArgs = wrapper.getArgs(); - if ((!decompilerArgs.isFsCaseSensitive() && !decompilerArgs.isRenameCaseSensitive()) - || !decompilerArgs.isRenameValid() || !decompilerArgs.isRenamePrintable()) { - JOptionPane.showMessageDialog( - this, - NLS.str("msg.rename_disabled", settings.getLangLocale()), - NLS.str("msg.rename_disabled_title", settings.getLangLocale()), - JOptionPane.INFORMATION_MESSAGE); - } JFileChooser fileChooser = new JFileChooser(); fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); fileChooser.setToolTipText(NLS.str("file.save_all_msg")); @@ -629,6 +620,7 @@ public class MainWindow extends JFrame { int ret = fileChooser.showSaveDialog(mainPanel); if (ret == JFileChooser.APPROVE_OPTION) { + JadxArgs decompilerArgs = wrapper.getArgs(); decompilerArgs.setExportAsGradleProject(export); if (export) { decompilerArgs.setSkipSources(false); diff --git a/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeArea.java b/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeArea.java index fe39d75f2..70eefab36 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeArea.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/codearea/CodeArea.java @@ -7,6 +7,7 @@ import java.awt.event.MouseEvent; import javax.swing.JPopupMenu; import javax.swing.event.PopupMenuEvent; +import javax.swing.text.BadLocationException; import org.fife.ui.rsyntaxtextarea.RSyntaxDocument; import org.fife.ui.rsyntaxtextarea.Token; @@ -178,6 +179,37 @@ public final class CodeArea extends AbstractCodeArea { return nodeCache.makeFrom(javaNode); } + @SuppressWarnings("deprecation") + public CodePosition getMouseCodePos() { + try { + Point mousePos = UiUtils.getMousePosition(this); + return buildCodePosFromOffset(this.viewToModel(mousePos)); + } catch (Exception e) { + LOG.error("Failed to get offset at mouse position", e); + return null; + } + } + + @Nullable + public CodePosition getCaretCodePos() { + try { + return buildCodePosFromOffset(getCaretPosition()); + } catch (Exception e) { + LOG.warn("Failed to get caret position", e); + return null; + } + } + + private CodePosition buildCodePosFromOffset(int offset) throws BadLocationException { + int start = getWordStart(offset); + if (start == -1) { + start = offset; + } + int line = getLineOfOffset(start); + int lineOffset = start - getLineStartOffset(line); + return new CodePosition(line + 1, lineOffset + 1, start); + } + public JNode getNodeUnderCaret() { int start = getWordStart(getCaretPosition()); if (start == -1) { diff --git a/jadx-gui/src/main/java/jadx/gui/ui/codearea/CommentAction.java b/jadx-gui/src/main/java/jadx/gui/ui/codearea/CommentAction.java index 3e93eb42b..4f27d88e5 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/codearea/CommentAction.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/codearea/CommentAction.java @@ -16,9 +16,9 @@ import jadx.api.JavaClass; import jadx.api.JavaMethod; import jadx.api.JavaNode; import jadx.api.data.ICodeComment; -import jadx.api.data.annotations.CustomOffsetRef; import jadx.api.data.annotations.InsnCodeOffset; import jadx.api.data.impl.JadxCodeComment; +import jadx.api.data.impl.JadxCodeRef; import jadx.api.data.impl.JadxNodeRef; import jadx.gui.treemodel.JClass; import jadx.gui.treemodel.JNode; @@ -123,13 +123,7 @@ public class CommentAction extends AbstractAction implements DefaultPopupMenuLis JadxNodeRef nodeRef = JadxNodeRef.forMth((JavaMethod) node); if (ann instanceof InsnCodeOffset) { int rawOffset = ((InsnCodeOffset) ann).getOffset(); - return new JadxCodeComment(nodeRef, "", rawOffset); - } - if (ann instanceof CustomOffsetRef) { - CustomOffsetRef customRef = (CustomOffsetRef) ann; - JadxCodeComment comment = new JadxCodeComment(nodeRef, "", customRef.getOffset()); - comment.setAttachType(customRef.getAttachType()); - return comment; + return new JadxCodeComment(nodeRef, JadxCodeRef.forInsn(rawOffset), ""); } } } catch (Exception e) { diff --git a/jadx-gui/src/main/java/jadx/gui/ui/codearea/MouseHoverHighlighter.java b/jadx-gui/src/main/java/jadx/gui/ui/codearea/MouseHoverHighlighter.java index 225ecbbd8..7ebf8ba2c 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/codearea/MouseHoverHighlighter.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/codearea/MouseHoverHighlighter.java @@ -12,6 +12,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jadx.api.JavaNode; +import jadx.gui.treemodel.JNode; +import jadx.gui.utils.JNodeCache; class MouseHoverHighlighter extends MouseMotionAdapter { private static final Logger LOG = LoggerFactory.getLogger(MouseHoverHighlighter.class); @@ -57,6 +59,7 @@ class MouseHoverHighlighter extends MouseMotionAdapter { removeHighlight(); tag = codeArea.getHighlighter().addHighlight(tokenOffset, token.getEndOffset(), this.highlighter); highlightedTokenOffset = tokenOffset; + updateToolTip(nodeAtOffset); return true; } catch (Exception exc) { LOG.error("Mouse hover highlight error", exc); @@ -69,6 +72,17 @@ class MouseHoverHighlighter extends MouseMotionAdapter { codeArea.getHighlighter().removeHighlight(tag); tag = null; highlightedTokenOffset = -1; + updateToolTip(null); } } + + private void updateToolTip(JavaNode node) { + if (node == null) { + codeArea.setToolTipText(null); + return; + } + JNodeCache nodeCache = codeArea.getMainWindow().getCacheObject().getNodeCache(); + JNode jNode = nodeCache.makeFrom(node); + codeArea.setToolTipText(jNode.makeLongString()); + } } diff --git a/jadx-gui/src/main/java/jadx/gui/ui/codearea/RenameAction.java b/jadx-gui/src/main/java/jadx/gui/ui/codearea/RenameAction.java index 41b8d04fa..c6f437db2 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/codearea/RenameAction.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/codearea/RenameAction.java @@ -26,8 +26,9 @@ public final class RenameAction extends JNodeMenuAction { public RenameAction(CodeArea codeArea) { super(NLS.str("popup.rename") + " (n)", codeArea); KeyStroke key = getKeyStroke(VK_N, 0); - codeArea.getInputMap().put(key, "trigger rename"); - codeArea.getActionMap().put("trigger rename", new AbstractAction() { + String renameActionId = "trigger rename"; + codeArea.getInputMap().put(key, renameActionId); + codeArea.getActionMap().put(renameActionId, new AbstractAction() { @Override public void actionPerformed(ActionEvent e) { node = codeArea.getNodeUnderCaret(); @@ -45,7 +46,7 @@ public final class RenameAction extends JNodeMenuAction { if (!node.canRename()) { UiUtils.showMessageBox(codeArea.getMainWindow(), NLS.str("msg.rename_node_failed", node.getJavaNode().getFullName())); - LOG.info("node can't be renamed"); + LOG.warn("Can't rename node: {}", node); return; } RenameDialog.rename(codeArea.getMainWindow(), node); diff --git a/jadx-gui/src/main/java/jadx/gui/ui/dialog/CommentDialog.java b/jadx-gui/src/main/java/jadx/gui/ui/dialog/CommentDialog.java index 55a74dc5f..e629810fa 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/dialog/CommentDialog.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/dialog/CommentDialog.java @@ -65,6 +65,7 @@ public class CommentDialog extends JDialog { Collections.sort(list); codeData.setComments(list); project.setCodeData(codeData); + codeArea.getMainWindow().getWrapper().getDecompiler().reloadCodeData(); } catch (Exception e) { LOG.error("Comment action failed", e); } @@ -85,8 +86,7 @@ public class CommentDialog extends JDialog { } for (ICodeComment comment : codeData.getComments()) { if (Objects.equals(comment.getNodeRef(), blankComment.getNodeRef()) - && comment.getOffset() == blankComment.getOffset() - && comment.getAttachType() == blankComment.getAttachType()) { + && Objects.equals(comment.getCodeRef(), blankComment.getCodeRef())) { return comment; } } @@ -120,8 +120,7 @@ public class CommentDialog extends JDialog { } return; } - ICodeComment newComment = new JadxCodeComment(comment.getNodeRef(), - newCommentStr, comment.getOffset(), comment.getAttachType()); + ICodeComment newComment = new JadxCodeComment(comment.getNodeRef(), comment.getCodeRef(), newCommentStr); if (updateComment) { updateCommentsData(codeArea, list -> { list.remove(comment); diff --git a/jadx-gui/src/main/java/jadx/gui/ui/dialog/RenameDialog.java b/jadx-gui/src/main/java/jadx/gui/ui/dialog/RenameDialog.java index 6aa20ac68..2aa9fb3b8 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/dialog/RenameDialog.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/dialog/RenameDialog.java @@ -5,10 +5,13 @@ import java.awt.Container; import java.awt.Dimension; import java.awt.FlowLayout; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.function.Consumer; import java.util.stream.Collectors; import javax.swing.BorderFactory; @@ -17,7 +20,6 @@ import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JDialog; import javax.swing.JLabel; -import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.SwingConstants; @@ -27,22 +29,21 @@ import org.jetbrains.annotations.NotNull; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import jadx.api.ICodeWriter; import jadx.api.JavaClass; -import jadx.api.JavaField; import jadx.api.JavaMethod; import jadx.api.JavaNode; -import jadx.core.deobf.DeobfPresets; -import jadx.core.dex.attributes.AType; -import jadx.core.dex.attributes.nodes.MethodOverrideAttr; -import jadx.core.dex.nodes.MethodNode; +import jadx.api.JavaVariable; +import jadx.api.data.ICodeRename; +import jadx.api.data.impl.JadxCodeData; +import jadx.api.data.impl.JadxCodeRef; +import jadx.api.data.impl.JadxCodeRename; +import jadx.api.data.impl.JadxNodeRef; import jadx.core.dex.nodes.RootNode; -import jadx.core.dex.nodes.VariableNode; -import jadx.core.dex.visitors.RenameVisitor; +import jadx.core.dex.visitors.rename.RenameVisitor; import jadx.core.utils.Utils; import jadx.core.utils.exceptions.JadxRuntimeException; import jadx.gui.jobs.TaskStatus; -import jadx.gui.settings.JadxSettings; +import jadx.gui.settings.JadxProject; import jadx.gui.treemodel.JClass; import jadx.gui.treemodel.JField; import jadx.gui.treemodel.JMethod; @@ -71,9 +72,6 @@ public class RenameDialog extends JDialog { private transient JTextField renameField; public static boolean rename(MainWindow mainWindow, JNode node) { - if (!checkSettings(mainWindow)) { - return false; - } RenameDialog renameDialog = new RenameDialog(mainWindow, node); renameDialog.setVisible(true); return true; @@ -87,108 +85,74 @@ public class RenameDialog extends JDialog { initUI(); } - public static boolean checkSettings(MainWindow mainWindow) { - StringBuilder errorMessage = new StringBuilder(); - errorMessage.append(NLS.str("msg.rename_disabled")).append(ICodeWriter.NL); - - JadxSettings settings = mainWindow.getSettings(); - boolean valid = true; - if (!settings.isDeobfuscationOn()) { - errorMessage.append(" - ").append(NLS.str("msg.rename_disabled_deobfuscation_disabled")).append(ICodeWriter.NL); - valid = false; - } - if (settings.isDeobfuscationForceSave()) { - errorMessage.append(" - ").append(NLS.str("msg.rename_disabled_force_rewrite_enabled")).append(ICodeWriter.NL); - valid = false; - } - if (valid) { - return true; - } - int result = JOptionPane.showConfirmDialog(mainWindow, errorMessage.toString(), - NLS.str("msg.rename_disabled_title"), JOptionPane.OK_CANCEL_OPTION); - if (result != JOptionPane.OK_OPTION) { - return false; - } - settings.setDeobfuscationOn(true); - settings.setDeobfuscationForceSave(false); - settings.sync(); - - mainWindow.reOpenFile(); - return false; // TODO: can't open dialog, 'node' is replaced with new one after reopen - } - - private void updateDeobfMap(DeobfPresets deobfPresets, String renameText) { - if (node instanceof JMethod) { - MethodNode mthNode = ((JavaMethod) node.getJavaNode()).getMethodNode(); - MethodOverrideAttr overrideAttr = mthNode.get(AType.METHOD_OVERRIDE); - if (overrideAttr != null) { - for (MethodNode relatedMth : overrideAttr.getRelatedMthNodes()) { - deobfPresets.getMthPresetMap().put(relatedMth.getMethodInfo().getRawFullId(), renameText); - } - } - deobfPresets.getMthPresetMap().put(mthNode.getMethodInfo().getRawFullId(), renameText); - } else if (node instanceof JField) { - JavaField javaField = (JavaField) node.getJavaNode(); - deobfPresets.getFldPresetMap().put(javaField.getFieldNode().getFieldInfo().getRawFullId(), renameText); - } else if (node instanceof JClass) { - JavaClass javaClass = (JavaClass) node.getJavaNode(); - deobfPresets.getClsPresetMap().put(javaClass.getRawName(), renameText); - } else if (node instanceof JPackage) { - deobfPresets.getPkgPresetMap().put(((JPackage) node).getFullName(), renameText); - } else if (node instanceof JVariable) { - VariableNode varNode = ((JVariable) node).getJavaVarNode().getVariableNode(); - deobfPresets.updateVariableName(varNode, renameText); - } - } - private void rename() { try { - String renameText = renameField.getText(); - if (renameText == null || renameText.length() == 0) { - return; - } - RootNode root = mainWindow.getWrapper().getDecompiler().getRoot(); - if (node == null) { - LOG.error("rename(): rootNode is null!"); - dispose(); - return; - } - if (!refreshDeobfMapFile(renameText, root)) { - LOG.error("rename(): refreshDeobfMapFile() failed!"); - dispose(); - return; - } - refreshState(root); + updateCodeRenames(set -> processRename(node, renameField.getText(), set)); + refreshState(); } catch (Exception e) { LOG.error("Rename failed", e); } dispose(); } - private boolean refreshDeobfMapFile(String renameText, RootNode root) { - DeobfPresets deobfPresets = DeobfPresets.build(root); - if (deobfPresets == null) { - return false; + private void processRename(JNode node, String newName, Set renames) { + JadxCodeRename rename = buildRename(node, newName, renames); + renames.remove(rename); + JavaNode javaNode = node.getJavaNode(); + if (javaNode != null) { + javaNode.removeAlias(); } - try { - deobfPresets.load(); - } catch (Exception e) { - LOG.error("rename(): readDeobfMap() failed"); - return false; + if (!newName.isEmpty()) { + renames.add(rename); } - updateDeobfMap(deobfPresets, renameText); - try { - deobfPresets.save(); - } catch (Exception e) { - LOG.error("rename(): writeDeobfMap() failed"); - return false; - } - return true; } - private void refreshState(RootNode rootNode) { - RenameVisitor renameVisitor = new RenameVisitor(); - renameVisitor.init(rootNode); + @NotNull + private JadxCodeRename buildRename(JNode node, String newName, Set renames) { + if (node instanceof JMethod) { + JavaMethod javaMethod = ((JMethod) node).getJavaMethod(); + List relatedMethods = javaMethod.getOverrideRelatedMethods(); + if (!relatedMethods.isEmpty()) { + for (JavaMethod relatedMethod : relatedMethods) { + renames.remove(new JadxCodeRename(JadxNodeRef.forMth(relatedMethod), "")); + } + } + return new JadxCodeRename(JadxNodeRef.forMth(javaMethod), newName); + } + if (node instanceof JField) { + return new JadxCodeRename(JadxNodeRef.forFld(((JField) node).getJavaField()), newName); + } + if (node instanceof JClass) { + return new JadxCodeRename(JadxNodeRef.forCls(((JClass) node).getCls()), newName); + } + if (node instanceof JPackage) { + return new JadxCodeRename(JadxNodeRef.forPkg(((JPackage) node).getFullName()), newName); + } + if (node instanceof JVariable) { + JavaVariable javaVar = ((JVariable) node).getJavaVarNode(); + return new JadxCodeRename(JadxNodeRef.forMth(javaVar.getMth()), JadxCodeRef.forVar(javaVar), newName); + } + throw new JadxRuntimeException("Failed to build rename node for: " + node); + } + + private void updateCodeRenames(Consumer> updater) { + JadxProject project = mainWindow.getProject(); + JadxCodeData codeData = project.getCodeData(); + if (codeData == null) { + codeData = new JadxCodeData(); + } + Set set = new HashSet<>(codeData.getRenames()); + updater.accept(set); + List list = new ArrayList<>(set); + Collections.sort(list); + codeData.setRenames(list); + project.setCodeData(codeData); + mainWindow.getWrapper().getDecompiler().reloadCodeData(); + } + + private void refreshState() { + RootNode rootNode = mainWindow.getWrapper().getDecompiler().getRoot(); + new RenameVisitor().init(rootNode); JNodeCache nodeCache = cache.getNodeCache(); JavaNode javaNode = node.getJavaNode(); diff --git a/jadx-gui/src/main/java/jadx/gui/utils/JNodeCache.java b/jadx-gui/src/main/java/jadx/gui/utils/JNodeCache.java index a54bd7147..8d4bc1752 100644 --- a/jadx-gui/src/main/java/jadx/gui/utils/JNodeCache.java +++ b/jadx-gui/src/main/java/jadx/gui/utils/JNodeCache.java @@ -53,7 +53,9 @@ public class JNodeCache { return new JField((JavaField) node, makeFrom(node.getDeclaringClass())); } if (node instanceof JavaVariable) { - return new JVariable((JavaVariable) node, makeFrom(node.getDeclaringClass())); + JavaVariable javaVar = (JavaVariable) node; + JMethod jMth = (JMethod) makeFrom(javaVar.getMth()); + return new JVariable(jMth, javaVar); } throw new JadxRuntimeException("Unknown type for JavaNode: " + node.getClass()); } diff --git a/jadx-gui/src/main/java/jadx/gui/utils/search/CommentsIndex.java b/jadx-gui/src/main/java/jadx/gui/utils/search/CommentsIndex.java index 7ae2cbdf3..0158a672a 100644 --- a/jadx-gui/src/main/java/jadx/gui/utils/search/CommentsIndex.java +++ b/jadx-gui/src/main/java/jadx/gui/utils/search/CommentsIndex.java @@ -21,6 +21,7 @@ import jadx.api.JavaField; import jadx.api.JavaMethod; import jadx.api.JavaNode; import jadx.api.data.ICodeComment; +import jadx.api.data.IJavaCodeRef; import jadx.api.data.IJavaNodeRef; import jadx.api.data.annotations.ICodeRawOffset; import jadx.gui.JadxWrapper; @@ -84,7 +85,7 @@ public class CommentsIndex { private @NotNull RefCommentNode getCommentNode(ICodeComment comment, JNode refNode) { IJavaNodeRef nodeRef = comment.getNodeRef(); - if (nodeRef.getType() == IJavaNodeRef.RefType.METHOD && comment.getOffset() > 0) { + if (nodeRef.getType() == IJavaNodeRef.RefType.METHOD && comment.getCodeRef() != null) { return new CodeCommentNode((JMethod) refNode, comment); } return new RefCommentNode(refNode, comment.getComment()); @@ -129,7 +130,8 @@ public class CommentsIndex { public CodeCommentNode(JMethod node, ICodeComment comment) { super(node, comment.getComment()); - this.offset = comment.getOffset(); + IJavaCodeRef codeRef = Objects.requireNonNull(comment.getCodeRef(), "Null comment code ref"); + this.offset = codeRef.getIndex(); } @Override diff --git a/jadx-gui/src/main/resources/i18n/Messages_de_DE.properties b/jadx-gui/src/main/resources/i18n/Messages_de_DE.properties index 886547d76..49efc9521 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_de_DE.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_de_DE.properties @@ -177,10 +177,6 @@ msg.language_changed=Neue Sprache wird beim nächsten Start der Anwendung angeze msg.index_not_initialized=Index nicht initialisiert, Suche wird deaktiviert! msg.project_error_title=Fehler msg.project_error=Projekt konnte nicht geladen werden -msg.rename_disabled_title=Umbenennen deaktiviert -msg.rename_disabled=Einige der Umbenennungseinstellungen sind deaktiviert, bitte beachten Sie dies. -msg.rename_disabled_force_rewrite_enabled=Deaktivieren Sie zum Umbenennen die Option "Deobfuscationskartendatei umschreiben erzwingen". -msg.rename_disabled_deobfuscation_disabled=Bitte aktivieren Sie die Umbenennung von Deobfuscation. msg.cmd_select_class_error=Klasse\n%s auswählen nicht möglich\nSie existiert nicht. msg.rename_node_disabled=Dieser Knotenpunkt kann nicht umbenannt werden msg.rename_node_failed=%s umbenennen nicht möglich diff --git a/jadx-gui/src/main/resources/i18n/Messages_en_US.properties b/jadx-gui/src/main/resources/i18n/Messages_en_US.properties index 21af2dded..c6adac799 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_en_US.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_en_US.properties @@ -177,10 +177,6 @@ msg.language_changed=New language will be displayed the next time application st msg.index_not_initialized=Index not initialized, search will be disabled! msg.project_error_title=Error msg.project_error=Project could not be loaded -msg.rename_disabled_title=Rename disabled -msg.rename_disabled=Some of rename settings are disabled, next options will be changed: -msg.rename_disabled_force_rewrite_enabled=Disable "Force rewrite deobfuscation map file" option. -msg.rename_disabled_deobfuscation_disabled=Enable deobfuscation. msg.cmd_select_class_error=Failed to select the class\n%s\nThe class does not exist. msg.rename_node_disabled=Can't rename this node msg.rename_node_failed=Can't rename %s diff --git a/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties b/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties index 3673a68c2..467b8813c 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_es_ES.properties @@ -177,10 +177,6 @@ msg.language_changed=El nuevo idioma se mostrará la próxima vez que la aplicac msg.index_not_initialized=Índice no inicializado, ¡la bósqueda se desactivará! #msg.project_error_title= #msg.project_error= -#msg.rename_disabled_title= -#msg.rename_disabled= -#msg.rename_disabled_force_rewrite_enabled= -#msg.rename_disabled_deobfuscation_disabled= #msg.cmd_select_class_error= #msg.rename_node_disabled= #msg.rename_node_failed= diff --git a/jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties b/jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties index 2bbfa2d3b..da08fe120 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_ko_KR.properties @@ -177,10 +177,6 @@ msg.language_changed=다음에 응용 프로그램이 시작되면 새 언어가 msg.index_not_initialized=인덱스가 초기화되지 않았습니다. 검색이 비활성화됩니다! msg.project_error_title=오류 msg.project_error=프로젝트를 로드 할 수 없습니다. -msg.rename_disabled_title=이름 변경 사용 안함 -msg.rename_disabled=일부 이름 바꾸기 설정이 비활성화되고 다음 옵션이 변경됩니다: -msg.rename_disabled_force_rewrite_enabled="난독 해제 맵 파일 강제 다시 쓰기" 옵션을 비활성화합니다. -msg.rename_disabled_deobfuscation_disabled=난독 해제 활성화 msg.cmd_select_class_error=클래스를 선택하지 못했습니다.\n%s\n클래스가 없습니다. msg.rename_node_disabled=이 노드의 이름을 바꿀 수 없습니다. msg.rename_node_failed=%s의 이름을 바꿀 수 없습니다. diff --git a/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties b/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties index d8dfe6136..7a0aedeb5 100644 --- a/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties +++ b/jadx-gui/src/main/resources/i18n/Messages_zh_CN.properties @@ -177,10 +177,6 @@ msg.language_changed=在下次启动时将会显示新的语言。 msg.index_not_initialized=索引尚未初始化,无法进行搜索! msg.project_error_title=错误 msg.project_error=项目无法加载 -msg.rename_disabled_title=重命名已禁用 -msg.rename_disabled=某些重命名设置已禁用,请将此考虑在内 -msg.rename_disabled_force_rewrite_enabled=请禁用“强制覆盖反重构映射文件”选项以重命名。 -msg.rename_disabled_deobfuscation_disabled=请启用反混淆以重命名。 msg.cmd_select_class_error=无法选择类\n%s\n该类不存在。 #msg.rename_node_disabled= #msg.rename_node_failed=