feat: rename without deobfuscation, save renames in project (#1076 #1022)

This commit is contained in:
Skylot
2021-10-26 20:06:14 +01:00
parent 82712776cc
commit dfdc14ea86
72 changed files with 1221 additions and 721 deletions
@@ -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);
@@ -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;
}
@@ -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();
@@ -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!
*/
@@ -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<JavaNode> getOverrideRelatedMethods() {
public List<JavaMethod> 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!
*/
@@ -17,4 +17,7 @@ public interface JavaNode {
int getDefPos();
List<JavaNode> getUseIn();
default void removeAlias() {
}
}
@@ -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<JavaNode> 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);
}
}
@@ -0,0 +1,8 @@
package jadx.api.data;
public enum CodeRefType {
MTH_ARG,
VAR,
CATCH,
INSN,
}
@@ -6,17 +6,8 @@ public interface ICodeComment extends Comparable<ICodeComment> {
IJavaNodeRef getNodeRef();
String getComment();
/**
* Instruction offset inside method
*/
int getOffset();
enum AttachType {
VAR_DECLARE
}
@Nullable
AttachType getAttachType();
IJavaCodeRef getCodeRef();
String getComment();
}
@@ -4,7 +4,7 @@ import java.util.List;
public interface ICodeData {
long getUpdateId();
List<ICodeComment> getComments();
List<ICodeRename> getRenames();
}
@@ -0,0 +1,13 @@
package jadx.api.data;
import org.jetbrains.annotations.Nullable;
public interface ICodeRename extends Comparable<ICodeRename> {
IJavaNodeRef getNodeRef();
@Nullable
IJavaCodeRef getCodeRef();
String getNewName();
}
@@ -0,0 +1,15 @@
package jadx.api.data;
import org.jetbrains.annotations.NotNull;
public interface IJavaCodeRef extends Comparable<IJavaCodeRef> {
CodeRefType getAttachType();
int getIndex();
@Override
default int compareTo(@NotNull IJavaCodeRef o) {
return Integer.compare(getIndex(), o.getIndex());
}
}
@@ -3,7 +3,7 @@ package jadx.api.data;
public interface IJavaNodeRef extends Comparable<IJavaNodeRef> {
enum RefType {
CLASS, FIELD, METHOD
CLASS, FIELD, METHOD, PKG
}
RefType getType();
@@ -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 + '}';
}
}
@@ -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() + '}';
}
}
@@ -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 + '}';
}
}
@@ -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<ICodeComment> 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 + '\''
+ '}';
}
}
@@ -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<ICodeComment> comments = Collections.emptyList();
@Override
public long getUpdateId() {
return updateId;
}
public void markUpdate() {
updateId = System.currentTimeMillis();
}
private List<ICodeRename> renames = Collections.emptyList();
@Override
public List<ICodeComment> getComments() {
@@ -26,24 +17,15 @@ public class JadxCodeData implements ICodeData {
}
public void setComments(List<ICodeComment> comments) {
markUpdate();
this.comments = comments;
}
@Override
public int hashCode() {
return Long.hashCode(updateId);
public List<ICodeRename> 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<ICodeRename> renames) {
this.renames = renames;
}
}
@@ -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
+ '}';
}
}
@@ -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());
}
}
@@ -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:
@@ -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());
@@ -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
}
+3 -1
View File
@@ -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());
}
@@ -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> 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) {
@@ -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()) {
@@ -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());
}
@@ -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<String, String> clsPresetMap = new HashMap<>();
private final Map<String, String> fldPresetMap = new HashMap<>();
private final Map<String, String> mthPresetMap = new HashMap<>();
private final Map<String, Set<String>> 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<String> 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<String, String> mthEntry : mthPresetMap.entrySet()) {
list.add(String.format("m %s = %s", mthEntry.getKey(), mthEntry.getValue()));
}
for (Map.Entry<String, Set<String>> 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<String> 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<String, String> getMthPresetMap() {
return mthPresetMap;
}
public Map<String, Set<String>> 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<String> indexSet = varPresetMap.computeIfAbsent(key, k -> new HashSet<>());
indexSet.remove(oldIndex);
indexSet.add(newIndex);
}
}
@@ -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<String> 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 <b>create</b> 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) {
@@ -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;
}
}
@@ -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);
}
@@ -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;
}
@@ -116,6 +116,17 @@ public final class ClassInfo implements Comparable<ClassInfo> {
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;
@@ -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);
}
@@ -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<MethodInfo> {
@@ -27,7 +23,6 @@ public final class MethodInfo implements Comparable<MethodInfo> {
private final int hash;
private String alias;
private Map<String, String> varNameMap;
private MethodInfo(ClassInfo declClass, String name, List<ArgType> args, ArgType retType) {
this.name = name;
@@ -158,33 +153,14 @@ public final class MethodInfo implements Comparable<MethodInfo> {
this.alias = alias;
}
public void removeAlias() {
this.alias = name;
}
public boolean hasAlias() {
return !name.equals(alias);
}
public synchronized void setVarNameMap(Set<String> 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();
}
@@ -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<SSAVar> 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
*/
@@ -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;
@@ -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();
}
@@ -0,0 +1,8 @@
package jadx.core.dex.nodes;
import jadx.api.data.ICodeData;
public interface ICodeDataUpdateListener {
void updated(ICodeData codeData);
}
@@ -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<MethodNode> useIn = Collections.emptyList();
private List<VariableNode> 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<VariableNode> 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;
@@ -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<IDexTreeVisitor> preDecompilePasses;
private final List<IDexTreeVisitor> passes;
private final List<ICodeDataUpdateListener> 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;
}
@@ -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;
}
}
@@ -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<String, List<ICodeComment>> 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<String, List<ICodeComment>> clsCommentsMap;
}
private List<ICodeComment> 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<ICodeComment> clsComments = commentsData.clsCommentsMap.get(cls.getClassInfo().getFullName());
if (clsComments == null) {
return Collections.emptyList();
}
return clsComments;
List<ICodeComment> clsComments = clsCommentsMap.get(cls.getClassInfo().getRawName());
if (clsComments == null) {
return Collections.emptyList();
}
return clsComments;
}
private static void updateCommentsData(ICodeData data, CommentsData commentsData) {
Map<String, List<ICodeComment>> map = new HashMap<>();
for (ICodeComment comment : data.getComments()) {
String declClsId = comment.getNodeRef().getDeclaringClass();
List<ICodeComment> 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();
}
}
@@ -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;
@@ -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<String, List<ICodeRename>> clsRenamesMap;
@Override
public void init(RootNode root) throws JadxException {
updateRenamesMap(root.getArgs().getCodeData());
root.registerCodeDataUpdateListener(this::updateRenamesMap);
}
@Override
public boolean visit(ClassNode cls) {
List<ICodeRename> renames = getRenames(cls);
if (!renames.isEmpty()) {
applyRenames(cls, renames);
}
cls.getInnerClasses().forEach(this::visit);
return false;
}
private static void applyRenames(ClassNode cls, List<ICodeRename> 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<RegisterArg> 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<ICodeRename> getRenames(ClassNode cls) {
if (clsRenamesMap == null) {
return Collections.emptyList();
}
List<ICodeRename> 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()));
}
}
}
@@ -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();
@@ -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<ICodeRename> renames) {
List<ClassNode> 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);
}
}
@@ -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(
@@ -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;
/**
@@ -23,7 +23,7 @@ public class JadxCodeInfoAssertions extends AbstractObjectAssert<JadxCodeInfoAss
public JadxCodeInfoAssertions checkCodeOffsets() {
long dupOffsetCount = actual.getAnnotations().values().stream()
.filter(o -> o instanceof ICodeRawOffset)
.filter(ICodeRawOffset.class::isInstance)
.collect(Collectors.groupingBy(o -> ((ICodeRawOffset) o).getOffset(), Collectors.toList()))
.values().stream()
.filter(list -> list.size() > 1)
@@ -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")
@@ -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));
@@ -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));
@@ -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));
@@ -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();
@@ -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<ICodeRename> 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");
}
}
@@ -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();
@@ -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;
@@ -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);
@@ -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) {
@@ -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) {
@@ -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());
}
}
@@ -26,8 +26,9 @@ public final class RenameAction extends JNodeMenuAction<JNode> {
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<JNode> {
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);
@@ -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);
@@ -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<ICodeRename> 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<ICodeRename> renames) {
if (node instanceof JMethod) {
JavaMethod javaMethod = ((JMethod) node).getJavaMethod();
List<JavaMethod> 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<Set<ICodeRename>> updater) {
JadxProject project = mainWindow.getProject();
JadxCodeData codeData = project.getCodeData();
if (codeData == null) {
codeData = new JadxCodeData();
}
Set<ICodeRename> set = new HashSet<>(codeData.getRenames());
updater.accept(set);
List<ICodeRename> 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();
@@ -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());
}
@@ -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
@@ -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
@@ -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
@@ -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=
@@ -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의 이름을 바꿀 수 없습니다.
@@ -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=