feat: input plugin for java bytecode
This commit is contained in:
@@ -29,8 +29,9 @@ jobs:
|
|||||||
name: Build with Gradle
|
name: Build with Gradle
|
||||||
env:
|
env:
|
||||||
TERM: dumb
|
TERM: dumb
|
||||||
|
TEST_INPUT_PLUGIN: dx
|
||||||
with:
|
with:
|
||||||
arguments: build dist copyExe --warning-mode=all
|
arguments: clean build dist copyExe --warning-mode=all
|
||||||
|
|
||||||
- name: Save bundle artifact
|
- name: Save bundle artifact
|
||||||
if: success() && github.event_name == 'push'
|
if: success() && github.event_name == 'push'
|
||||||
|
|||||||
+1
-6
@@ -16,12 +16,7 @@ java-8:
|
|||||||
java-11:
|
java-11:
|
||||||
stage: test
|
stage: test
|
||||||
image: openjdk:11
|
image: openjdk:11
|
||||||
script: ./gradlew clean build dist --warning-mode=all
|
script: ./gradlew clean build dist copyExe --warning-mode=all
|
||||||
|
|
||||||
java-15:
|
|
||||||
stage: test
|
|
||||||
image: openjdk:15
|
|
||||||
script: ./gradlew clean build dist --warning-mode=all
|
|
||||||
|
|
||||||
java-latest:
|
java-latest:
|
||||||
stage: test
|
stage: test
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ dependencies {
|
|||||||
implementation(project(':jadx-core'))
|
implementation(project(':jadx-core'))
|
||||||
|
|
||||||
runtimeOnly(project(':jadx-plugins:jadx-dex-input'))
|
runtimeOnly(project(':jadx-plugins:jadx-dex-input'))
|
||||||
|
runtimeOnly(project(':jadx-plugins:jadx-java-input'))
|
||||||
runtimeOnly(project(':jadx-plugins:jadx-smali-input'))
|
runtimeOnly(project(':jadx-plugins:jadx-smali-input'))
|
||||||
runtimeOnly(project(':jadx-plugins:jadx-java-convert'))
|
|
||||||
|
|
||||||
implementation 'com.beust:jcommander:1.81'
|
implementation 'com.beust:jcommander:1.81'
|
||||||
implementation 'ch.qos.logback:logback-classic:1.2.5'
|
implementation 'ch.qos.logback:logback-classic:1.2.5'
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ dependencies {
|
|||||||
testRuntimeOnly(project(':jadx-plugins:jadx-dex-input'))
|
testRuntimeOnly(project(':jadx-plugins:jadx-dex-input'))
|
||||||
testRuntimeOnly(project(':jadx-plugins:jadx-smali-input'))
|
testRuntimeOnly(project(':jadx-plugins:jadx-smali-input'))
|
||||||
testRuntimeOnly(project(':jadx-plugins:jadx-java-convert'))
|
testRuntimeOnly(project(':jadx-plugins:jadx-java-convert'))
|
||||||
|
testRuntimeOnly(project(':jadx-plugins:jadx-java-input'))
|
||||||
}
|
}
|
||||||
|
|
||||||
test {
|
test {
|
||||||
|
|||||||
@@ -576,6 +576,10 @@ public final class JadxDecompiler implements Closeable {
|
|||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public JadxPluginManager getPluginManager() {
|
||||||
|
return pluginManager;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "jadx decompiler " + getVersion();
|
return "jadx decompiler " + getVersion();
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ public final class JavaClass implements JavaNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public synchronized String getSmali() {
|
public synchronized String getSmali() {
|
||||||
return cls.getSmali();
|
return cls.getDisassembledCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -15,13 +15,6 @@ public class Consts {
|
|||||||
public static final String CLASS_ENUM = "java.lang.Enum";
|
public static final String CLASS_ENUM = "java.lang.Enum";
|
||||||
|
|
||||||
public static final String CLASS_STRING_BUILDER = "java.lang.StringBuilder";
|
public static final String CLASS_STRING_BUILDER = "java.lang.StringBuilder";
|
||||||
|
|
||||||
public static final String DALVIK_ANNOTATION_PKG = "Ldalvik/annotation/";
|
|
||||||
public static final String DALVIK_SIGNATURE = "Ldalvik/annotation/Signature;";
|
|
||||||
public static final String DALVIK_INNER_CLASS = "Ldalvik/annotation/InnerClass;";
|
|
||||||
public static final String DALVIK_THROWS = "Ldalvik/annotation/Throws;";
|
|
||||||
public static final String DALVIK_ANNOTATION_DEFAULT = "Ldalvik/annotation/AnnotationDefault;";
|
|
||||||
|
|
||||||
public static final String OVERRIDE_ANNOTATION = "Ljava/lang/Override;";
|
public static final String OVERRIDE_ANNOTATION = "Ljava/lang/Override;";
|
||||||
|
|
||||||
public static final String DEFAULT_PACKAGE_NAME = "defpackage";
|
public static final String DEFAULT_PACKAGE_NAME = "defpackage";
|
||||||
|
|||||||
@@ -8,14 +8,15 @@ import java.util.Map.Entry;
|
|||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import jadx.api.ICodeWriter;
|
import jadx.api.ICodeWriter;
|
||||||
import jadx.api.plugins.input.data.IFieldData;
|
import jadx.api.plugins.input.data.IFieldRef;
|
||||||
import jadx.api.plugins.input.data.annotations.EncodedValue;
|
import jadx.api.plugins.input.data.annotations.EncodedValue;
|
||||||
import jadx.api.plugins.input.data.annotations.IAnnotation;
|
import jadx.api.plugins.input.data.annotations.IAnnotation;
|
||||||
|
import jadx.api.plugins.input.data.attributes.JadxAttrType;
|
||||||
|
import jadx.api.plugins.input.data.attributes.types.AnnotationDefaultAttr;
|
||||||
|
import jadx.api.plugins.input.data.attributes.types.AnnotationsAttr;
|
||||||
|
import jadx.api.plugins.input.data.attributes.types.MethodParamsAttr;
|
||||||
import jadx.core.Consts;
|
import jadx.core.Consts;
|
||||||
import jadx.core.dex.attributes.AType;
|
|
||||||
import jadx.core.dex.attributes.IAttributeNode;
|
import jadx.core.dex.attributes.IAttributeNode;
|
||||||
import jadx.core.dex.attributes.annotations.AnnotationsList;
|
|
||||||
import jadx.core.dex.attributes.annotations.MethodParameters;
|
|
||||||
import jadx.core.dex.info.FieldInfo;
|
import jadx.core.dex.info.FieldInfo;
|
||||||
import jadx.core.dex.instructions.args.ArgType;
|
import jadx.core.dex.instructions.args.ArgType;
|
||||||
import jadx.core.dex.nodes.ClassNode;
|
import jadx.core.dex.nodes.ClassNode;
|
||||||
@@ -47,12 +48,12 @@ public class AnnotationGen {
|
|||||||
add(field, code);
|
add(field, code);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addForParameter(ICodeWriter code, MethodParameters paramsAnnotations, int n) {
|
public void addForParameter(ICodeWriter code, MethodParamsAttr paramsAnnotations, int n) {
|
||||||
List<AnnotationsList> paramList = paramsAnnotations.getParamList();
|
List<AnnotationsAttr> paramList = paramsAnnotations.getParamList();
|
||||||
if (n >= paramList.size()) {
|
if (n >= paramList.size()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
AnnotationsList aList = paramList.get(n);
|
AnnotationsAttr aList = paramList.get(n);
|
||||||
if (aList == null || aList.isEmpty()) {
|
if (aList == null || aList.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -63,13 +64,13 @@ public class AnnotationGen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void add(IAttributeNode node, ICodeWriter code) {
|
private void add(IAttributeNode node, ICodeWriter code) {
|
||||||
AnnotationsList aList = node.get(AType.ANNOTATION_LIST);
|
AnnotationsAttr aList = node.get(JadxAttrType.ANNOTATION_LIST);
|
||||||
if (aList == null || aList.isEmpty()) {
|
if (aList == null || aList.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (IAnnotation a : aList.getAll()) {
|
for (IAnnotation a : aList.getAll()) {
|
||||||
String aCls = a.getAnnotationClass();
|
String aCls = a.getAnnotationClass();
|
||||||
if (!aCls.startsWith(Consts.DALVIK_ANNOTATION_PKG) && !aCls.equals(Consts.OVERRIDE_ANNOTATION)) {
|
if (!aCls.equals(Consts.OVERRIDE_ANNOTATION)) {
|
||||||
code.startLine();
|
code.startLine();
|
||||||
formatAnnotation(code, a);
|
formatAnnotation(code, a);
|
||||||
}
|
}
|
||||||
@@ -131,16 +132,12 @@ public class AnnotationGen {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public EncodedValue getAnnotationDefaultValue(String name) {
|
public EncodedValue getAnnotationDefaultValue(MethodNode mth) {
|
||||||
IAnnotation an = cls.getAnnotation(Consts.DALVIK_ANNOTATION_DEFAULT);
|
AnnotationDefaultAttr defaultAttr = mth.get(JadxAttrType.ANNOTATION_DEFAULT);
|
||||||
if (an != null) {
|
if (defaultAttr == null) {
|
||||||
EncodedValue defValue = an.getDefaultValue();
|
return null;
|
||||||
if (defValue != null) {
|
|
||||||
IAnnotation defAnnotation = (IAnnotation) defValue.getValue();
|
|
||||||
return defAnnotation.getValues().get(name);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return null;
|
return defaultAttr.getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: refactor this boilerplate code
|
// TODO: refactor this boilerplate code
|
||||||
@@ -188,9 +185,9 @@ public class AnnotationGen {
|
|||||||
case ENCODED_ENUM:
|
case ENCODED_ENUM:
|
||||||
case ENCODED_FIELD:
|
case ENCODED_FIELD:
|
||||||
// must be a static field
|
// must be a static field
|
||||||
if (value instanceof IFieldData) {
|
if (value instanceof IFieldRef) {
|
||||||
FieldInfo field = FieldInfo.fromData(root, (IFieldData) value);
|
FieldInfo fieldInfo = FieldInfo.fromRef(root, (IFieldRef) value);
|
||||||
InsnGen.makeStaticFieldAccess(code, field, classGen);
|
InsnGen.makeStaticFieldAccess(code, fieldInfo, classGen);
|
||||||
} else if (value instanceof FieldInfo) {
|
} else if (value instanceof FieldInfo) {
|
||||||
InsnGen.makeStaticFieldAccess(code, (FieldInfo) value, classGen);
|
InsnGen.makeStaticFieldAccess(code, (FieldInfo) value, classGen);
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -20,11 +20,12 @@ import jadx.api.JadxArgs;
|
|||||||
import jadx.api.plugins.input.data.AccessFlags;
|
import jadx.api.plugins.input.data.AccessFlags;
|
||||||
import jadx.api.plugins.input.data.annotations.EncodedType;
|
import jadx.api.plugins.input.data.annotations.EncodedType;
|
||||||
import jadx.api.plugins.input.data.annotations.EncodedValue;
|
import jadx.api.plugins.input.data.annotations.EncodedValue;
|
||||||
|
import jadx.api.plugins.input.data.attributes.JadxAttrType;
|
||||||
import jadx.core.Consts;
|
import jadx.core.Consts;
|
||||||
import jadx.core.dex.attributes.AFlag;
|
import jadx.core.dex.attributes.AFlag;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.AttrNode;
|
import jadx.core.dex.attributes.AttrNode;
|
||||||
import jadx.core.dex.attributes.fldinit.FieldInitAttr;
|
import jadx.core.dex.attributes.FieldInitInsnAttr;
|
||||||
import jadx.core.dex.attributes.nodes.EnumClassAttr;
|
import jadx.core.dex.attributes.nodes.EnumClassAttr;
|
||||||
import jadx.core.dex.attributes.nodes.EnumClassAttr.EnumField;
|
import jadx.core.dex.attributes.nodes.EnumClassAttr.EnumField;
|
||||||
import jadx.core.dex.attributes.nodes.JadxError;
|
import jadx.core.dex.attributes.nodes.JadxError;
|
||||||
@@ -33,6 +34,7 @@ import jadx.core.dex.attributes.nodes.SkipMethodArgsAttr;
|
|||||||
import jadx.core.dex.info.AccessInfo;
|
import jadx.core.dex.info.AccessInfo;
|
||||||
import jadx.core.dex.info.ClassInfo;
|
import jadx.core.dex.info.ClassInfo;
|
||||||
import jadx.core.dex.instructions.args.ArgType;
|
import jadx.core.dex.instructions.args.ArgType;
|
||||||
|
import jadx.core.dex.instructions.args.LiteralArg;
|
||||||
import jadx.core.dex.instructions.args.PrimitiveType;
|
import jadx.core.dex.instructions.args.PrimitiveType;
|
||||||
import jadx.core.dex.instructions.mods.ConstructorInsn;
|
import jadx.core.dex.instructions.mods.ConstructorInsn;
|
||||||
import jadx.core.dex.nodes.ClassNode;
|
import jadx.core.dex.nodes.ClassNode;
|
||||||
@@ -41,6 +43,7 @@ import jadx.core.dex.nodes.InsnNode;
|
|||||||
import jadx.core.dex.nodes.MethodNode;
|
import jadx.core.dex.nodes.MethodNode;
|
||||||
import jadx.core.dex.nodes.RootNode;
|
import jadx.core.dex.nodes.RootNode;
|
||||||
import jadx.core.utils.CodeGenUtils;
|
import jadx.core.utils.CodeGenUtils;
|
||||||
|
import jadx.core.utils.EncodedValueUtils;
|
||||||
import jadx.core.utils.ErrorsCounter;
|
import jadx.core.utils.ErrorsCounter;
|
||||||
import jadx.core.utils.Utils;
|
import jadx.core.utils.Utils;
|
||||||
import jadx.core.utils.android.AndroidResourcesUtils;
|
import jadx.core.utils.android.AndroidResourcesUtils;
|
||||||
@@ -396,21 +399,30 @@ public class ClassGen {
|
|||||||
code.add(' ');
|
code.add(' ');
|
||||||
code.attachDefinition(f);
|
code.attachDefinition(f);
|
||||||
code.add(f.getAlias());
|
code.add(f.getAlias());
|
||||||
FieldInitAttr fv = f.get(AType.FIELD_INIT);
|
|
||||||
if (fv != null) {
|
FieldInitInsnAttr initInsnAttr = f.get(AType.FIELD_INIT_INSN);
|
||||||
|
if (initInsnAttr != null) {
|
||||||
|
InsnGen insnGen = makeInsnGen(initInsnAttr.getInsnMth());
|
||||||
code.add(" = ");
|
code.add(" = ");
|
||||||
if (fv.isConst()) {
|
addInsnBody(insnGen, code, initInsnAttr.getInsn());
|
||||||
EncodedValue encodedValue = fv.getEncodedValue();
|
} else {
|
||||||
if (encodedValue.getType() == EncodedType.ENCODED_NULL) {
|
EncodedValue constVal = f.get(JadxAttrType.CONSTANT_VALUE);
|
||||||
|
if (constVal != null) {
|
||||||
|
code.add(" = ");
|
||||||
|
if (constVal.getType() == EncodedType.ENCODED_NULL) {
|
||||||
code.add(TypeGen.literalToString(0, f.getType(), cls, fallback));
|
code.add(TypeGen.literalToString(0, f.getType(), cls, fallback));
|
||||||
} else {
|
} else {
|
||||||
if (!AndroidResourcesUtils.handleResourceFieldValue(cls, code, encodedValue)) {
|
Object val = EncodedValueUtils.convertToConstValue(constVal);
|
||||||
annotationGen.encodeValue(cls.root(), code, encodedValue);
|
if (val instanceof LiteralArg) {
|
||||||
|
long lit = ((LiteralArg) val).getLiteral();
|
||||||
|
if (!AndroidResourcesUtils.handleResourceFieldValue(cls, code, lit, f.getType())) {
|
||||||
|
// force literal type to be same as field (java bytecode can use different type)
|
||||||
|
code.add(TypeGen.literalToString(lit, f.getType(), cls, fallback));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
annotationGen.encodeValue(cls.root(), code, constVal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (fv.isInsn()) {
|
|
||||||
InsnGen insnGen = makeInsnGen(fv.getInsnMth());
|
|
||||||
addInsnBody(insnGen, code, fv.getInsn());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
code.add(';');
|
code.add(';');
|
||||||
|
|||||||
@@ -398,11 +398,15 @@ public class InsnGen {
|
|||||||
ArgType arrayType = ((NewArrayNode) insn).getArrayType();
|
ArgType arrayType = ((NewArrayNode) insn).getArrayType();
|
||||||
code.add("new ");
|
code.add("new ");
|
||||||
useType(code, arrayType.getArrayRootElement());
|
useType(code, arrayType.getArrayRootElement());
|
||||||
code.add('[');
|
int k = 0;
|
||||||
addArg(code, insn.getArg(0));
|
int argsCount = insn.getArgsCount();
|
||||||
code.add(']');
|
for (; k < argsCount; k++) {
|
||||||
|
code.add('[');
|
||||||
|
addArg(code, insn.getArg(k), false);
|
||||||
|
code.add(']');
|
||||||
|
}
|
||||||
int dim = arrayType.getArrayDimension();
|
int dim = arrayType.getArrayDimension();
|
||||||
for (int i = 0; i < dim - 1; i++) {
|
for (; k < dim - 1; k++) {
|
||||||
code.add("[]");
|
code.add("[]");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -572,7 +576,7 @@ public class InsnGen {
|
|||||||
|
|
||||||
case FILL_ARRAY_DATA:
|
case FILL_ARRAY_DATA:
|
||||||
fallbackOnlyInsn(insn);
|
fallbackOnlyInsn(insn);
|
||||||
code.add("fill-array " + insn.toString());
|
code.add("fill-array " + insn);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SWITCH_DATA:
|
case SWITCH_DATA:
|
||||||
@@ -580,6 +584,18 @@ public class InsnGen {
|
|||||||
code.add(insn.toString());
|
code.add(insn.toString());
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case MOVE_MULTI:
|
||||||
|
fallbackOnlyInsn(insn);
|
||||||
|
code.add("move-multi: ");
|
||||||
|
int len = insn.getArgsCount();
|
||||||
|
for (int i = 0; i < len - 1; i += 2) {
|
||||||
|
addArg(code, insn.getArg(i));
|
||||||
|
code.add(" = ");
|
||||||
|
addArg(code, insn.getArg(i + 1));
|
||||||
|
code.add("; ");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new CodegenException(mth, "Unknown instruction: " + insn.getType());
|
throw new CodegenException(mth, "Unknown instruction: " + insn.getType());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,11 +13,12 @@ import jadx.api.ICodeWriter;
|
|||||||
import jadx.api.data.annotations.InsnCodeOffset;
|
import jadx.api.data.annotations.InsnCodeOffset;
|
||||||
import jadx.api.plugins.input.data.AccessFlags;
|
import jadx.api.plugins.input.data.AccessFlags;
|
||||||
import jadx.api.plugins.input.data.annotations.EncodedValue;
|
import jadx.api.plugins.input.data.annotations.EncodedValue;
|
||||||
|
import jadx.api.plugins.input.data.attributes.JadxAttrType;
|
||||||
|
import jadx.api.plugins.input.data.attributes.types.MethodParamsAttr;
|
||||||
import jadx.core.Consts;
|
import jadx.core.Consts;
|
||||||
import jadx.core.Jadx;
|
import jadx.core.Jadx;
|
||||||
import jadx.core.dex.attributes.AFlag;
|
import jadx.core.dex.attributes.AFlag;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.annotations.MethodParameters;
|
|
||||||
import jadx.core.dex.attributes.nodes.JadxError;
|
import jadx.core.dex.attributes.nodes.JadxError;
|
||||||
import jadx.core.dex.attributes.nodes.JumpInfo;
|
import jadx.core.dex.attributes.nodes.JumpInfo;
|
||||||
import jadx.core.dex.attributes.nodes.MethodOverrideAttr;
|
import jadx.core.dex.attributes.nodes.MethodOverrideAttr;
|
||||||
@@ -39,7 +40,6 @@ import jadx.core.utils.CodeGenUtils;
|
|||||||
import jadx.core.utils.InsnUtils;
|
import jadx.core.utils.InsnUtils;
|
||||||
import jadx.core.utils.Utils;
|
import jadx.core.utils.Utils;
|
||||||
import jadx.core.utils.exceptions.CodegenException;
|
import jadx.core.utils.exceptions.CodegenException;
|
||||||
import jadx.core.utils.exceptions.DecodeException;
|
|
||||||
import jadx.core.utils.exceptions.JadxOverflowException;
|
import jadx.core.utils.exceptions.JadxOverflowException;
|
||||||
|
|
||||||
import static jadx.core.codegen.MethodGen.FallbackOption.BLOCK_DUMP;
|
import static jadx.core.codegen.MethodGen.FallbackOption.BLOCK_DUMP;
|
||||||
@@ -103,6 +103,9 @@ public class MethodGen {
|
|||||||
if (clsAccFlags.isAnnotation()) {
|
if (clsAccFlags.isAnnotation()) {
|
||||||
ai = ai.remove(AccessFlags.PUBLIC);
|
ai = ai.remove(AccessFlags.PUBLIC);
|
||||||
}
|
}
|
||||||
|
if (mth.getMethodInfo().isConstructor() && mth.getParentClass().isEnum()) {
|
||||||
|
ai = ai.remove(AccessInfo.VISIBILITY_FLAGS);
|
||||||
|
}
|
||||||
|
|
||||||
if (mth.getMethodInfo().hasAlias() && !ai.isConstructor()) {
|
if (mth.getMethodInfo().hasAlias() && !ai.isConstructor()) {
|
||||||
CodeGenUtils.addRenamedComment(code, mth, mth.getName());
|
CodeGenUtils.addRenamedComment(code, mth, mth.getName());
|
||||||
@@ -113,9 +116,6 @@ public class MethodGen {
|
|||||||
|
|
||||||
code.startLineWithNum(mth.getSourceLine());
|
code.startLineWithNum(mth.getSourceLine());
|
||||||
code.add(ai.makeString());
|
code.add(ai.makeString());
|
||||||
if (Consts.DEBUG) {
|
|
||||||
code.add(mth.isVirtual() ? "/* virtual */ " : "/* direct */ ");
|
|
||||||
}
|
|
||||||
if (clsAccFlags.isInterface() && !mth.isNoCode() && !mth.getAccessFlags().isStatic()) {
|
if (clsAccFlags.isInterface() && !mth.isNoCode() && !mth.getAccessFlags().isStatic()) {
|
||||||
// add 'default' for method with code in interface
|
// add 'default' for method with code in interface
|
||||||
code.add("default ");
|
code.add("default ");
|
||||||
@@ -153,9 +153,9 @@ public class MethodGen {
|
|||||||
|
|
||||||
annotationGen.addThrows(mth, code);
|
annotationGen.addThrows(mth, code);
|
||||||
|
|
||||||
// add default value if in annotation class
|
// add default value for annotation class
|
||||||
if (mth.getParentClass().getAccessFlags().isAnnotation()) {
|
if (mth.getParentClass().getAccessFlags().isAnnotation()) {
|
||||||
EncodedValue def = annotationGen.getAnnotationDefaultValue(mth.getName());
|
EncodedValue def = annotationGen.getAnnotationDefaultValue(mth);
|
||||||
if (def != null) {
|
if (def != null) {
|
||||||
code.add(" default ");
|
code.add(" default ");
|
||||||
annotationGen.encodeValue(mth.root(), code, def);
|
annotationGen.encodeValue(mth.root(), code, def);
|
||||||
@@ -181,7 +181,7 @@ public class MethodGen {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void addMethodArguments(ICodeWriter code, List<RegisterArg> args) {
|
private void addMethodArguments(ICodeWriter code, List<RegisterArg> args) {
|
||||||
MethodParameters paramsAnnotation = mth.get(AType.ANNOTATION_MTH_PARAMETERS);
|
MethodParamsAttr paramsAnnotation = mth.get(JadxAttrType.ANNOTATION_MTH_PARAMETERS);
|
||||||
int i = 0;
|
int i = 0;
|
||||||
Iterator<RegisterArg> it = args.iterator();
|
Iterator<RegisterArg> it = args.iterator();
|
||||||
while (it.hasNext()) {
|
while (it.hasNext()) {
|
||||||
@@ -189,7 +189,7 @@ public class MethodGen {
|
|||||||
SSAVar ssaVar = mthArg.getSVar();
|
SSAVar ssaVar = mthArg.getSVar();
|
||||||
CodeVar var;
|
CodeVar var;
|
||||||
if (ssaVar == null) {
|
if (ssaVar == null) {
|
||||||
// null for abstract or interface methods
|
// abstract or interface methods
|
||||||
var = CodeVar.fromMthArg(mthArg, classGen.isFallbackMode());
|
var = CodeVar.fromMthArg(mthArg, classGen.isFallbackMode());
|
||||||
} else {
|
} else {
|
||||||
var = ssaVar.getCodeVar();
|
var = ssaVar.getCodeVar();
|
||||||
@@ -291,17 +291,21 @@ public class MethodGen {
|
|||||||
|
|
||||||
public void addFallbackMethodCode(ICodeWriter code, FallbackOption fallbackOption) {
|
public void addFallbackMethodCode(ICodeWriter code, FallbackOption fallbackOption) {
|
||||||
if (fallbackOption != FALLBACK_MODE) {
|
if (fallbackOption != FALLBACK_MODE) {
|
||||||
// load original instructions
|
List<JadxError> errors = mth.getAll(AType.JADX_ERROR); // preserve error before unload
|
||||||
try {
|
try {
|
||||||
|
// load original instructions
|
||||||
mth.unload();
|
mth.unload();
|
||||||
mth.load();
|
mth.load();
|
||||||
for (IDexTreeVisitor visitor : Jadx.getFallbackPassesList()) {
|
for (IDexTreeVisitor visitor : Jadx.getFallbackPassesList()) {
|
||||||
DepthTraversal.visit(visitor, mth);
|
DepthTraversal.visit(visitor, mth);
|
||||||
}
|
}
|
||||||
} catch (DecodeException e) {
|
errors.forEach(err -> mth.addAttr(AType.JADX_ERROR, err));
|
||||||
|
} catch (Exception e) {
|
||||||
LOG.error("Error reload instructions in fallback mode:", e);
|
LOG.error("Error reload instructions in fallback mode:", e);
|
||||||
code.startLine("// Can't load method instructions: " + e.getMessage());
|
code.startLine("// Can't load method instructions: " + e.getMessage());
|
||||||
return;
|
return;
|
||||||
|
} finally {
|
||||||
|
errors.forEach(err -> mth.addAttr(AType.JADX_ERROR, err));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
InsnNode[] insnArr = mth.getInstructions();
|
InsnNode[] insnArr = mth.getInstructions();
|
||||||
|
|||||||
@@ -12,9 +12,10 @@ import jadx.api.ICodeWriter;
|
|||||||
import jadx.api.data.ICodeComment;
|
import jadx.api.data.ICodeComment;
|
||||||
import jadx.api.data.annotations.CustomOffsetRef;
|
import jadx.api.data.annotations.CustomOffsetRef;
|
||||||
import jadx.api.data.annotations.InsnCodeOffset;
|
import jadx.api.data.annotations.InsnCodeOffset;
|
||||||
|
import jadx.api.plugins.input.data.annotations.EncodedValue;
|
||||||
|
import jadx.api.plugins.input.data.attributes.JadxAttrType;
|
||||||
import jadx.core.dex.attributes.AFlag;
|
import jadx.core.dex.attributes.AFlag;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.fldinit.FieldInitAttr;
|
|
||||||
import jadx.core.dex.attributes.nodes.DeclareVariablesAttr;
|
import jadx.core.dex.attributes.nodes.DeclareVariablesAttr;
|
||||||
import jadx.core.dex.attributes.nodes.ForceReturnAttr;
|
import jadx.core.dex.attributes.nodes.ForceReturnAttr;
|
||||||
import jadx.core.dex.attributes.nodes.LoopLabelAttr;
|
import jadx.core.dex.attributes.nodes.LoopLabelAttr;
|
||||||
@@ -291,12 +292,9 @@ public class RegionGen extends InsnGen {
|
|||||||
} else {
|
} else {
|
||||||
staticField(code, fn.getFieldInfo());
|
staticField(code, fn.getFieldInfo());
|
||||||
// print original value, sometimes replaced with incorrect field
|
// print original value, sometimes replaced with incorrect field
|
||||||
FieldInitAttr valueAttr = fn.get(AType.FIELD_INIT);
|
EncodedValue constVal = fn.get(JadxAttrType.CONSTANT_VALUE);
|
||||||
if (valueAttr != null && valueAttr.isConst()) {
|
if (constVal != null && constVal.getValue() != null) {
|
||||||
Object value = valueAttr.getEncodedValue().getValue();
|
code.add(" /* ").add(constVal.getValue().toString()).add(" */");
|
||||||
if (value != null) {
|
|
||||||
code.add(" /* ").add(value.toString()).add(" */");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (k instanceof Integer) {
|
} else if (k instanceof Integer) {
|
||||||
|
|||||||
@@ -9,10 +9,11 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import jadx.api.JadxArgs;
|
import jadx.api.JadxArgs;
|
||||||
|
import jadx.api.plugins.input.data.attributes.JadxAttrType;
|
||||||
|
import jadx.api.plugins.input.data.attributes.types.SourceFileAttr;
|
||||||
import jadx.core.dex.attributes.AFlag;
|
import jadx.core.dex.attributes.AFlag;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.nodes.MethodOverrideAttr;
|
import jadx.core.dex.attributes.nodes.MethodOverrideAttr;
|
||||||
import jadx.core.dex.attributes.nodes.SourceFileAttr;
|
|
||||||
import jadx.core.dex.info.ClassInfo;
|
import jadx.core.dex.info.ClassInfo;
|
||||||
import jadx.core.dex.info.FieldInfo;
|
import jadx.core.dex.info.FieldInfo;
|
||||||
import jadx.core.dex.info.MethodInfo;
|
import jadx.core.dex.info.MethodInfo;
|
||||||
@@ -443,7 +444,7 @@ public class Deobfuscator {
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private String getAliasFromSourceFile(ClassNode cls) {
|
private String getAliasFromSourceFile(ClassNode cls) {
|
||||||
SourceFileAttr sourceFileAttr = cls.get(AType.SOURCE_FILE);
|
SourceFileAttr sourceFileAttr = cls.get(JadxAttrType.SOURCE_FILE);
|
||||||
if (sourceFileAttr == null) {
|
if (sourceFileAttr == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -468,7 +469,7 @@ public class Deobfuscator {
|
|||||||
if (otherCls != null) {
|
if (otherCls != null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
cls.remove(AType.SOURCE_FILE);
|
cls.remove(JadxAttrType.SOURCE_FILE);
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,7 @@
|
|||||||
package jadx.core.dex.attributes;
|
package jadx.core.dex.attributes;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import jadx.api.plugins.input.data.attributes.IJadxAttrType;
|
||||||
import java.util.HashSet;
|
import jadx.api.plugins.input.data.attributes.IJadxAttribute;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import jadx.core.dex.attributes.annotations.AnnotationsList;
|
|
||||||
import jadx.core.dex.attributes.annotations.MethodParameters;
|
|
||||||
import jadx.core.dex.attributes.fldinit.FieldInitAttr;
|
|
||||||
import jadx.core.dex.attributes.nodes.ClassTypeVarsAttr;
|
import jadx.core.dex.attributes.nodes.ClassTypeVarsAttr;
|
||||||
import jadx.core.dex.attributes.nodes.DeclareVariablesAttr;
|
import jadx.core.dex.attributes.nodes.DeclareVariablesAttr;
|
||||||
import jadx.core.dex.attributes.nodes.EdgeInsnAttr;
|
import jadx.core.dex.attributes.nodes.EdgeInsnAttr;
|
||||||
@@ -28,7 +23,6 @@ import jadx.core.dex.attributes.nodes.PhiListAttr;
|
|||||||
import jadx.core.dex.attributes.nodes.RegDebugInfoAttr;
|
import jadx.core.dex.attributes.nodes.RegDebugInfoAttr;
|
||||||
import jadx.core.dex.attributes.nodes.RenameReasonAttr;
|
import jadx.core.dex.attributes.nodes.RenameReasonAttr;
|
||||||
import jadx.core.dex.attributes.nodes.SkipMethodArgsAttr;
|
import jadx.core.dex.attributes.nodes.SkipMethodArgsAttr;
|
||||||
import jadx.core.dex.attributes.nodes.SourceFileAttr;
|
|
||||||
import jadx.core.dex.nodes.IMethodDetails;
|
import jadx.core.dex.nodes.IMethodDetails;
|
||||||
import jadx.core.dex.trycatch.CatchAttr;
|
import jadx.core.dex.trycatch.CatchAttr;
|
||||||
import jadx.core.dex.trycatch.ExcHandlerAttr;
|
import jadx.core.dex.trycatch.ExcHandlerAttr;
|
||||||
@@ -40,14 +34,12 @@ import jadx.core.dex.trycatch.SplitterBlockAttr;
|
|||||||
*
|
*
|
||||||
* @param <T> attribute class implementation
|
* @param <T> attribute class implementation
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("InstantiationOfUtilityClass")
|
public final class AType<T extends IJadxAttribute> implements IJadxAttrType<T> {
|
||||||
public class AType<T extends IAttribute> {
|
|
||||||
|
|
||||||
// class, method, field, insn
|
// class, method, field, insn
|
||||||
public static final AType<AttrList<String>> CODE_COMMENTS = new AType<>();
|
public static final AType<AttrList<String>> CODE_COMMENTS = new AType<>();
|
||||||
|
|
||||||
// class, method, field
|
// class, method, field
|
||||||
public static final AType<AnnotationsList> ANNOTATION_LIST = new AType<>();
|
|
||||||
public static final AType<RenameReasonAttr> RENAME_REASON = new AType<>();
|
public static final AType<RenameReasonAttr> RENAME_REASON = new AType<>();
|
||||||
|
|
||||||
// class, method
|
// class, method
|
||||||
@@ -56,19 +48,17 @@ public class AType<T extends IAttribute> {
|
|||||||
public static final AType<AttrList<String>> COMMENTS = new AType<>(); // any additional info about decompilation
|
public static final AType<AttrList<String>> COMMENTS = new AType<>(); // any additional info about decompilation
|
||||||
|
|
||||||
// class
|
// class
|
||||||
public static final AType<SourceFileAttr> SOURCE_FILE = new AType<>();
|
|
||||||
public static final AType<EnumClassAttr> ENUM_CLASS = new AType<>();
|
public static final AType<EnumClassAttr> ENUM_CLASS = new AType<>();
|
||||||
public static final AType<EnumMapAttr> ENUM_MAP = new AType<>();
|
public static final AType<EnumMapAttr> ENUM_MAP = new AType<>();
|
||||||
public static final AType<ClassTypeVarsAttr> CLASS_TYPE_VARS = new AType<>();
|
public static final AType<ClassTypeVarsAttr> CLASS_TYPE_VARS = new AType<>();
|
||||||
|
|
||||||
// field
|
// field
|
||||||
public static final AType<FieldInitAttr> FIELD_INIT = new AType<>();
|
public static final AType<FieldInitInsnAttr> FIELD_INIT_INSN = new AType<>();
|
||||||
public static final AType<FieldReplaceAttr> FIELD_REPLACE = new AType<>();
|
public static final AType<FieldReplaceAttr> FIELD_REPLACE = new AType<>();
|
||||||
|
|
||||||
// method
|
// method
|
||||||
public static final AType<LocalVarsDebugInfoAttr> LOCAL_VARS_DEBUG_INFO = new AType<>();
|
public static final AType<LocalVarsDebugInfoAttr> LOCAL_VARS_DEBUG_INFO = new AType<>();
|
||||||
public static final AType<MethodInlineAttr> METHOD_INLINE = new AType<>();
|
public static final AType<MethodInlineAttr> METHOD_INLINE = new AType<>();
|
||||||
public static final AType<MethodParameters> ANNOTATION_MTH_PARAMETERS = new AType<>();
|
|
||||||
public static final AType<SkipMethodArgsAttr> SKIP_MTH_ARGS = new AType<>();
|
public static final AType<SkipMethodArgsAttr> SKIP_MTH_ARGS = new AType<>();
|
||||||
public static final AType<MethodOverrideAttr> METHOD_OVERRIDE = new AType<>();
|
public static final AType<MethodOverrideAttr> METHOD_OVERRIDE = new AType<>();
|
||||||
public static final AType<MethodTypeVarsAttr> METHOD_TYPE_VARS = new AType<>();
|
public static final AType<MethodTypeVarsAttr> METHOD_TYPE_VARS = new AType<>();
|
||||||
@@ -96,14 +86,4 @@ public class AType<T extends IAttribute> {
|
|||||||
|
|
||||||
// register
|
// register
|
||||||
public static final AType<RegDebugInfoAttr> REG_DEBUG_INFO = new AType<>();
|
public static final AType<RegDebugInfoAttr> REG_DEBUG_INFO = new AType<>();
|
||||||
|
|
||||||
public static final Set<AType<?>> SKIP_ON_UNLOAD = new HashSet<>(Arrays.asList(
|
|
||||||
SOURCE_FILE,
|
|
||||||
ANNOTATION_LIST,
|
|
||||||
ANNOTATION_MTH_PARAMETERS,
|
|
||||||
FIELD_INIT,
|
|
||||||
FIELD_REPLACE,
|
|
||||||
METHOD_INLINE,
|
|
||||||
METHOD_OVERRIDE,
|
|
||||||
SKIP_MTH_ARGS));
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,14 +4,16 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import jadx.api.ICodeWriter;
|
import jadx.api.ICodeWriter;
|
||||||
|
import jadx.api.plugins.input.data.attributes.IJadxAttrType;
|
||||||
|
import jadx.api.plugins.input.data.attributes.IJadxAttribute;
|
||||||
import jadx.core.utils.Utils;
|
import jadx.core.utils.Utils;
|
||||||
|
|
||||||
public class AttrList<T> implements IAttribute {
|
public class AttrList<T> implements IJadxAttribute {
|
||||||
|
|
||||||
private final AType<AttrList<T>> type;
|
private final IJadxAttrType<AttrList<T>> type;
|
||||||
private final List<T> list = new ArrayList<>();
|
private final List<T> list = new ArrayList<>();
|
||||||
|
|
||||||
public AttrList(AType<AttrList<T>> type) {
|
public AttrList(IJadxAttrType<AttrList<T>> type) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -20,7 +22,7 @@ public class AttrList<T> implements IAttribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AType<AttrList<T>> getType() {
|
public IJadxAttrType<AttrList<T>> getAttrType() {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ package jadx.core.dex.attributes;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import jadx.api.plugins.input.data.annotations.IAnnotation;
|
import jadx.api.plugins.input.data.annotations.IAnnotation;
|
||||||
|
import jadx.api.plugins.input.data.attributes.IJadxAttrType;
|
||||||
|
import jadx.api.plugins.input.data.attributes.IJadxAttribute;
|
||||||
|
|
||||||
public abstract class AttrNode implements IAttributeNode {
|
public abstract class AttrNode implements IAttributeNode {
|
||||||
|
|
||||||
@@ -16,12 +18,17 @@ public abstract class AttrNode implements IAttributeNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addAttr(IAttribute attr) {
|
public void addAttr(IJadxAttribute attr) {
|
||||||
initStorage().add(attr);
|
initStorage().add(attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> void addAttr(AType<AttrList<T>> type, T obj) {
|
public void addAttrs(List<IJadxAttribute> list) {
|
||||||
|
initStorage().add(list);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> void addAttr(IJadxAttrType<AttrList<T>> type, T obj) {
|
||||||
initStorage().add(type, obj);
|
initStorage().add(type, obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,8 +41,8 @@ public abstract class AttrNode implements IAttributeNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T extends IAttribute> void copyAttributeFrom(AttrNode attrNode, AType<T> attrType) {
|
public <T extends IJadxAttribute> void copyAttributeFrom(AttrNode attrNode, AType<T> attrType) {
|
||||||
IAttribute attr = attrNode.get(attrType);
|
IJadxAttribute attr = attrNode.get(attrType);
|
||||||
if (attr != null) {
|
if (attr != null) {
|
||||||
this.addAttr(attr);
|
this.addAttr(attr);
|
||||||
}
|
}
|
||||||
@@ -45,7 +52,7 @@ public abstract class AttrNode implements IAttributeNode {
|
|||||||
* Remove attribute in this node, add copy from other if exists
|
* Remove attribute in this node, add copy from other if exists
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public <T extends IAttribute> void rewriteAttributeFrom(AttrNode attrNode, AType<T> attrType) {
|
public <T extends IJadxAttribute> void rewriteAttributeFrom(AttrNode attrNode, AType<T> attrType) {
|
||||||
remove(attrType);
|
remove(attrType);
|
||||||
copyAttributeFrom(attrNode, attrType);
|
copyAttributeFrom(attrNode, attrType);
|
||||||
}
|
}
|
||||||
@@ -71,12 +78,12 @@ public abstract class AttrNode implements IAttributeNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T extends IAttribute> boolean contains(AType<T> type) {
|
public <T extends IJadxAttribute> boolean contains(IJadxAttrType<T> type) {
|
||||||
return storage.contains(type);
|
return storage.contains(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T extends IAttribute> T get(AType<T> type) {
|
public <T extends IJadxAttribute> T get(IJadxAttrType<T> type) {
|
||||||
return storage.get(type);
|
return storage.get(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,7 +93,7 @@ public abstract class AttrNode implements IAttributeNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> List<T> getAll(AType<AttrList<T>> type) {
|
public <T> List<T> getAll(IJadxAttrType<AttrList<T>> type) {
|
||||||
return storage.getAll(type);
|
return storage.getAll(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,13 +104,13 @@ public abstract class AttrNode implements IAttributeNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T extends IAttribute> void remove(AType<T> type) {
|
public <T extends IJadxAttribute> void remove(IJadxAttrType<T> type) {
|
||||||
storage.remove(type);
|
storage.remove(type);
|
||||||
unloadIfEmpty();
|
unloadIfEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeAttr(IAttribute attr) {
|
public void removeAttr(IJadxAttribute attr) {
|
||||||
storage.remove(attr);
|
storage.remove(attr);
|
||||||
unloadIfEmpty();
|
unloadIfEmpty();
|
||||||
}
|
}
|
||||||
@@ -115,7 +122,7 @@ public abstract class AttrNode implements IAttributeNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove all attribute with exceptions from {@link AType#SKIP_ON_UNLOAD}
|
* Remove all attribute
|
||||||
*/
|
*/
|
||||||
public void unloadAttributes() {
|
public void unloadAttributes() {
|
||||||
if (storage == EMPTY_ATTR_STORAGE) {
|
if (storage == EMPTY_ATTR_STORAGE) {
|
||||||
|
|||||||
@@ -9,7 +9,10 @@ import java.util.Map;
|
|||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import jadx.api.plugins.input.data.annotations.IAnnotation;
|
import jadx.api.plugins.input.data.annotations.IAnnotation;
|
||||||
import jadx.core.dex.attributes.annotations.AnnotationsList;
|
import jadx.api.plugins.input.data.attributes.IJadxAttrType;
|
||||||
|
import jadx.api.plugins.input.data.attributes.IJadxAttribute;
|
||||||
|
import jadx.api.plugins.input.data.attributes.JadxAttrType;
|
||||||
|
import jadx.api.plugins.input.data.attributes.types.AnnotationsAttr;
|
||||||
import jadx.core.utils.Utils;
|
import jadx.core.utils.Utils;
|
||||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||||
|
|
||||||
@@ -28,22 +31,34 @@ public class AttributeStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private final Set<AFlag> flags;
|
private final Set<AFlag> flags;
|
||||||
private Map<AType<?>, IAttribute> attributes;
|
private Map<IJadxAttrType<?>, IJadxAttribute> attributes;
|
||||||
|
|
||||||
public AttributeStorage() {
|
public AttributeStorage() {
|
||||||
flags = EnumSet.noneOf(AFlag.class);
|
flags = EnumSet.noneOf(AFlag.class);
|
||||||
attributes = Collections.emptyMap();
|
attributes = Collections.emptyMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AttributeStorage(List<IJadxAttribute> attributesList) {
|
||||||
|
this();
|
||||||
|
add(attributesList);
|
||||||
|
}
|
||||||
|
|
||||||
public void add(AFlag flag) {
|
public void add(AFlag flag) {
|
||||||
flags.add(flag);
|
flags.add(flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void add(IAttribute attr) {
|
public void add(IJadxAttribute attr) {
|
||||||
writeAttributes().put(attr.getType(), attr);
|
writeAttributes().put(attr.getAttrType(), attr);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> void add(AType<AttrList<T>> type, T obj) {
|
public void add(List<IJadxAttribute> list) {
|
||||||
|
Map<IJadxAttrType<?>, IJadxAttribute> map = writeAttributes();
|
||||||
|
for (IJadxAttribute attr : list) {
|
||||||
|
map.put(attr.getAttrType(), attr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> void add(IJadxAttrType<AttrList<T>> type, T obj) {
|
||||||
AttrList<T> list = get(type);
|
AttrList<T> list = get(type);
|
||||||
if (list == null) {
|
if (list == null) {
|
||||||
list = new AttrList<>(type);
|
list = new AttrList<>(type);
|
||||||
@@ -61,21 +76,21 @@ public class AttributeStorage {
|
|||||||
return flags.contains(flag);
|
return flags.contains(flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T extends IAttribute> boolean contains(AType<T> type) {
|
public <T extends IJadxAttribute> boolean contains(IJadxAttrType<T> type) {
|
||||||
return attributes.containsKey(type);
|
return attributes.containsKey(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <T extends IAttribute> T get(AType<T> type) {
|
public <T extends IJadxAttribute> T get(IJadxAttrType<T> type) {
|
||||||
return (T) attributes.get(type);
|
return (T) attributes.get(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IAnnotation getAnnotation(String cls) {
|
public IAnnotation getAnnotation(String cls) {
|
||||||
AnnotationsList aList = get(AType.ANNOTATION_LIST);
|
AnnotationsAttr aList = get(JadxAttrType.ANNOTATION_LIST);
|
||||||
return aList == null ? null : aList.get(cls);
|
return aList == null ? null : aList.get(cls);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> List<T> getAll(AType<AttrList<T>> type) {
|
public <T> List<T> getAll(IJadxAttrType<AttrList<T>> type) {
|
||||||
AttrList<T> attrList = get(type);
|
AttrList<T> attrList = get(type);
|
||||||
if (attrList == null) {
|
if (attrList == null) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
@@ -87,23 +102,23 @@ public class AttributeStorage {
|
|||||||
flags.remove(flag);
|
flags.remove(flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T extends IAttribute> void remove(AType<T> type) {
|
public <T extends IJadxAttribute> void remove(IJadxAttrType<T> type) {
|
||||||
if (!attributes.isEmpty()) {
|
if (!attributes.isEmpty()) {
|
||||||
attributes.remove(type);
|
attributes.remove(type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void remove(IAttribute attr) {
|
public void remove(IJadxAttribute attr) {
|
||||||
if (!attributes.isEmpty()) {
|
if (!attributes.isEmpty()) {
|
||||||
AType<? extends IAttribute> type = attr.getType();
|
IJadxAttrType<? extends IJadxAttribute> type = attr.getAttrType();
|
||||||
IAttribute a = attributes.get(type);
|
IJadxAttribute a = attributes.get(type);
|
||||||
if (a == attr) {
|
if (a == attr) {
|
||||||
attributes.remove(type);
|
attributes.remove(type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<AType<?>, IAttribute> writeAttributes() {
|
private Map<IJadxAttrType<?>, IJadxAttribute> writeAttributes() {
|
||||||
if (attributes.isEmpty()) {
|
if (attributes.isEmpty()) {
|
||||||
attributes = new IdentityHashMap<>(5);
|
attributes = new IdentityHashMap<>(5);
|
||||||
}
|
}
|
||||||
@@ -121,8 +136,7 @@ public class AttributeStorage {
|
|||||||
if (attributes.isEmpty()) {
|
if (attributes.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Set<AType<?>> skipOnUnload = AType.SKIP_ON_UNLOAD;
|
attributes.entrySet().removeIf(entry -> !entry.getValue().keepLoaded());
|
||||||
attributes.keySet().removeIf(attrType -> !skipOnUnload.contains(attrType));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getAttributeStrings() {
|
public List<String> getAttributeStrings() {
|
||||||
@@ -134,7 +148,7 @@ public class AttributeStorage {
|
|||||||
for (AFlag a : flags) {
|
for (AFlag a : flags) {
|
||||||
list.add(a.toString());
|
list.add(a.toString());
|
||||||
}
|
}
|
||||||
for (IAttribute a : attributes.values()) {
|
for (IJadxAttribute a : attributes.values()) {
|
||||||
list.add(a.toAttrString());
|
list.add(a.toAttrString());
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import jadx.api.plugins.input.data.annotations.IAnnotation;
|
import jadx.api.plugins.input.data.annotations.IAnnotation;
|
||||||
|
import jadx.api.plugins.input.data.attributes.IJadxAttrType;
|
||||||
|
import jadx.api.plugins.input.data.attributes.IJadxAttribute;
|
||||||
|
|
||||||
public final class EmptyAttrStorage extends AttributeStorage {
|
public final class EmptyAttrStorage extends AttributeStorage {
|
||||||
|
|
||||||
@@ -13,12 +15,12 @@ public final class EmptyAttrStorage extends AttributeStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T extends IAttribute> boolean contains(AType<T> type) {
|
public <T extends IJadxAttribute> boolean contains(IJadxAttrType<T> type) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T extends IAttribute> T get(AType<T> type) {
|
public <T extends IJadxAttribute> T get(IJadxAttrType<T> type) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,7 +30,7 @@ public final class EmptyAttrStorage extends AttributeStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> List<T> getAll(AType<AttrList<T>> type) {
|
public <T> List<T> getAll(IJadxAttrType<AttrList<T>> type) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -43,12 +45,12 @@ public final class EmptyAttrStorage extends AttributeStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T extends IAttribute> void remove(AType<T> type) {
|
public <T extends IJadxAttribute> void remove(IJadxAttrType<T> type) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void remove(IAttribute attr) {
|
public void remove(IJadxAttribute attr) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+8
-7
@@ -1,32 +1,33 @@
|
|||||||
package jadx.core.dex.attributes.fldinit;
|
package jadx.core.dex.attributes;
|
||||||
|
|
||||||
|
import jadx.api.plugins.input.data.attributes.IJadxAttrType;
|
||||||
|
import jadx.api.plugins.input.data.attributes.IJadxAttribute;
|
||||||
|
import jadx.api.plugins.input.data.attributes.PinnedAttribute;
|
||||||
import jadx.core.dex.nodes.InsnNode;
|
import jadx.core.dex.nodes.InsnNode;
|
||||||
import jadx.core.dex.nodes.MethodNode;
|
import jadx.core.dex.nodes.MethodNode;
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull;
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
public final class FieldInitInsnAttr extends FieldInitAttr {
|
public final class FieldInitInsnAttr extends PinnedAttribute {
|
||||||
private final MethodNode mth;
|
private final MethodNode mth;
|
||||||
private final InsnNode insn;
|
private final InsnNode insn;
|
||||||
|
|
||||||
FieldInitInsnAttr(MethodNode mth, InsnNode insn) {
|
public FieldInitInsnAttr(MethodNode mth, InsnNode insn) {
|
||||||
this.mth = requireNonNull(mth);
|
this.mth = requireNonNull(mth);
|
||||||
this.insn = requireNonNull(insn);
|
this.insn = requireNonNull(insn);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public InsnNode getInsn() {
|
public InsnNode getInsn() {
|
||||||
return insn;
|
return insn;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public MethodNode getInsnMth() {
|
public MethodNode getInsnMth() {
|
||||||
return mth;
|
return mth;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isInsn() {
|
public IJadxAttrType<? extends IJadxAttribute> getAttrType() {
|
||||||
return true;
|
return AType.FIELD_INIT_INSN;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
package jadx.core.dex.attributes;
|
|
||||||
|
|
||||||
public interface IAttribute {
|
|
||||||
AType<? extends IAttribute> getType();
|
|
||||||
|
|
||||||
default String toAttrString() {
|
|
||||||
return this.toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,36 +3,40 @@ package jadx.core.dex.attributes;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import jadx.api.plugins.input.data.annotations.IAnnotation;
|
import jadx.api.plugins.input.data.annotations.IAnnotation;
|
||||||
|
import jadx.api.plugins.input.data.attributes.IJadxAttrType;
|
||||||
|
import jadx.api.plugins.input.data.attributes.IJadxAttribute;
|
||||||
|
|
||||||
public interface IAttributeNode {
|
public interface IAttributeNode {
|
||||||
|
|
||||||
void add(AFlag flag);
|
void add(AFlag flag);
|
||||||
|
|
||||||
void addAttr(IAttribute attr);
|
void addAttr(IJadxAttribute attr);
|
||||||
|
|
||||||
<T> void addAttr(AType<AttrList<T>> type, T obj);
|
void addAttrs(List<IJadxAttribute> list);
|
||||||
|
|
||||||
|
<T> void addAttr(IJadxAttrType<AttrList<T>> type, T obj);
|
||||||
|
|
||||||
void copyAttributesFrom(AttrNode attrNode);
|
void copyAttributesFrom(AttrNode attrNode);
|
||||||
|
|
||||||
<T extends IAttribute> void copyAttributeFrom(AttrNode attrNode, AType<T> attrType);
|
<T extends IJadxAttribute> void copyAttributeFrom(AttrNode attrNode, AType<T> attrType);
|
||||||
|
|
||||||
<T extends IAttribute> void rewriteAttributeFrom(AttrNode attrNode, AType<T> attrType);
|
<T extends IJadxAttribute> void rewriteAttributeFrom(AttrNode attrNode, AType<T> attrType);
|
||||||
|
|
||||||
boolean contains(AFlag flag);
|
boolean contains(AFlag flag);
|
||||||
|
|
||||||
<T extends IAttribute> boolean contains(AType<T> type);
|
<T extends IJadxAttribute> boolean contains(IJadxAttrType<T> type);
|
||||||
|
|
||||||
<T extends IAttribute> T get(AType<T> type);
|
<T extends IJadxAttribute> T get(IJadxAttrType<T> type);
|
||||||
|
|
||||||
IAnnotation getAnnotation(String cls);
|
IAnnotation getAnnotation(String cls);
|
||||||
|
|
||||||
<T> List<T> getAll(AType<AttrList<T>> type);
|
<T> List<T> getAll(IJadxAttrType<AttrList<T>> type);
|
||||||
|
|
||||||
void remove(AFlag flag);
|
void remove(AFlag flag);
|
||||||
|
|
||||||
<T extends IAttribute> void remove(AType<T> type);
|
<T extends IJadxAttribute> void remove(IJadxAttrType<T> type);
|
||||||
|
|
||||||
void removeAttr(IAttribute attr);
|
void removeAttr(IJadxAttribute attr);
|
||||||
|
|
||||||
void clearAttributes();
|
void clearAttributes();
|
||||||
|
|
||||||
|
|||||||
@@ -1,68 +0,0 @@
|
|||||||
package jadx.core.dex.attributes.annotations;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import jadx.api.plugins.input.data.annotations.IAnnotation;
|
|
||||||
import jadx.core.dex.attributes.AType;
|
|
||||||
import jadx.core.dex.attributes.IAttribute;
|
|
||||||
import jadx.core.dex.nodes.ICodeNode;
|
|
||||||
import jadx.core.utils.Utils;
|
|
||||||
|
|
||||||
public class AnnotationsList implements IAttribute {
|
|
||||||
|
|
||||||
public static void attach(ICodeNode node, List<IAnnotation> annotationList) {
|
|
||||||
AnnotationsList attrList = pack(annotationList);
|
|
||||||
if (attrList != null) {
|
|
||||||
node.addAttr(attrList);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public static AnnotationsList pack(List<IAnnotation> annotationList) {
|
|
||||||
if (annotationList.isEmpty()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
Map<String, IAnnotation> annMap = new HashMap<>(annotationList.size());
|
|
||||||
for (IAnnotation ann : annotationList) {
|
|
||||||
annMap.put(ann.getAnnotationClass(), ann);
|
|
||||||
}
|
|
||||||
return new AnnotationsList(annMap);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Map<String, IAnnotation> map;
|
|
||||||
|
|
||||||
public AnnotationsList(Map<String, IAnnotation> map) {
|
|
||||||
this.map = map;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IAnnotation get(String className) {
|
|
||||||
return map.get(className);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Collection<IAnnotation> getAll() {
|
|
||||||
return map.values();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int size() {
|
|
||||||
return map.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return map.isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AType<AnnotationsList> getType() {
|
|
||||||
return AType.ANNOTATION_LIST;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return Utils.listToString(map.values());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
package jadx.core.dex.attributes.annotations;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import jadx.api.plugins.input.data.annotations.IAnnotation;
|
|
||||||
import jadx.core.dex.attributes.AType;
|
|
||||||
import jadx.core.dex.attributes.IAttribute;
|
|
||||||
import jadx.core.dex.nodes.ICodeNode;
|
|
||||||
import jadx.core.utils.Utils;
|
|
||||||
|
|
||||||
public class MethodParameters implements IAttribute {
|
|
||||||
|
|
||||||
public static void attach(ICodeNode node, List<List<IAnnotation>> annotationRefList) {
|
|
||||||
if (annotationRefList.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
List<AnnotationsList> list = new ArrayList<>(annotationRefList.size());
|
|
||||||
for (List<IAnnotation> annList : annotationRefList) {
|
|
||||||
list.add(AnnotationsList.pack(annList));
|
|
||||||
}
|
|
||||||
node.addAttr(new MethodParameters(list));
|
|
||||||
}
|
|
||||||
|
|
||||||
private final List<AnnotationsList> paramList;
|
|
||||||
|
|
||||||
public MethodParameters(List<AnnotationsList> paramsList) {
|
|
||||||
this.paramList = paramsList;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<AnnotationsList> getParamList() {
|
|
||||||
return paramList;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AType<MethodParameters> getType() {
|
|
||||||
return AType.ANNOTATION_MTH_PARAMETERS;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return Utils.listToString(paramList);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
package jadx.core.dex.attributes.fldinit;
|
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import jadx.api.plugins.input.data.annotations.EncodedValue;
|
|
||||||
import jadx.core.dex.attributes.AType;
|
|
||||||
import jadx.core.dex.attributes.IAttribute;
|
|
||||||
import jadx.core.dex.nodes.InsnNode;
|
|
||||||
import jadx.core.dex.nodes.MethodNode;
|
|
||||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
|
||||||
|
|
||||||
public abstract class FieldInitAttr implements IAttribute {
|
|
||||||
|
|
||||||
public static FieldInitAttr constValue(EncodedValue value) {
|
|
||||||
if (Objects.equals(value, EncodedValue.NULL)) {
|
|
||||||
return FieldInitConstAttr.NULL_VALUE;
|
|
||||||
}
|
|
||||||
return new FieldInitConstAttr(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static FieldInitAttr insnValue(MethodNode mth, InsnNode insn) {
|
|
||||||
return new FieldInitInsnAttr(mth, insn);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isConst() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isInsn() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public EncodedValue getEncodedValue() {
|
|
||||||
throw new JadxRuntimeException("Wrong init type");
|
|
||||||
}
|
|
||||||
|
|
||||||
public InsnNode getInsn() {
|
|
||||||
throw new JadxRuntimeException("Wrong init type");
|
|
||||||
}
|
|
||||||
|
|
||||||
public MethodNode getInsnMth() {
|
|
||||||
throw new JadxRuntimeException("Wrong init type");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AType<FieldInitAttr> getType() {
|
|
||||||
return AType.FIELD_INIT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
package jadx.core.dex.attributes.fldinit;
|
|
||||||
|
|
||||||
import jadx.api.plugins.input.data.annotations.EncodedValue;
|
|
||||||
|
|
||||||
import static java.util.Objects.requireNonNull;
|
|
||||||
|
|
||||||
public final class FieldInitConstAttr extends FieldInitAttr {
|
|
||||||
public static final FieldInitAttr NULL_VALUE = new FieldInitConstAttr(EncodedValue.NULL);
|
|
||||||
|
|
||||||
private final EncodedValue value;
|
|
||||||
|
|
||||||
FieldInitConstAttr(EncodedValue value) {
|
|
||||||
this.value = requireNonNull(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public EncodedValue getEncodedValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isConst() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "INIT{" + value + '}';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,11 +4,11 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import jadx.api.plugins.input.data.attributes.IJadxAttribute;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.IAttribute;
|
|
||||||
import jadx.core.dex.instructions.args.ArgType;
|
import jadx.core.dex.instructions.args.ArgType;
|
||||||
|
|
||||||
public class ClassTypeVarsAttr implements IAttribute {
|
public class ClassTypeVarsAttr implements IJadxAttribute {
|
||||||
public static final ClassTypeVarsAttr EMPTY = new ClassTypeVarsAttr(Collections.emptyList(), Collections.emptyMap());
|
public static final ClassTypeVarsAttr EMPTY = new ClassTypeVarsAttr(Collections.emptyList(), Collections.emptyMap());
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -40,7 +40,7 @@ public class ClassTypeVarsAttr implements IAttribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AType<ClassTypeVarsAttr> getType() {
|
public AType<ClassTypeVarsAttr> getAttrType() {
|
||||||
return AType.CLASS_TYPE_VARS;
|
return AType.CLASS_TYPE_VARS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,15 +3,15 @@ package jadx.core.dex.attributes.nodes;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import jadx.api.plugins.input.data.attributes.IJadxAttribute;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.IAttribute;
|
|
||||||
import jadx.core.dex.instructions.args.CodeVar;
|
import jadx.core.dex.instructions.args.CodeVar;
|
||||||
import jadx.core.utils.Utils;
|
import jadx.core.utils.Utils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List of variables to be declared at region start.
|
* List of variables to be declared at region start.
|
||||||
*/
|
*/
|
||||||
public class DeclareVariablesAttr implements IAttribute {
|
public class DeclareVariablesAttr implements IJadxAttribute {
|
||||||
|
|
||||||
private final List<CodeVar> vars = new ArrayList<>();
|
private final List<CodeVar> vars = new ArrayList<>();
|
||||||
|
|
||||||
@@ -24,7 +24,7 @@ public class DeclareVariablesAttr implements IAttribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AType<DeclareVariablesAttr> getType() {
|
public AType<DeclareVariablesAttr> getAttrType() {
|
||||||
return AType.DECLARE_VARIABLES;
|
return AType.DECLARE_VARIABLES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ package jadx.core.dex.attributes.nodes;
|
|||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import jadx.api.plugins.input.data.attributes.IJadxAttribute;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.AttrList;
|
import jadx.core.dex.attributes.AttrList;
|
||||||
import jadx.core.dex.attributes.IAttribute;
|
|
||||||
import jadx.core.dex.nodes.BlockNode;
|
import jadx.core.dex.nodes.BlockNode;
|
||||||
import jadx.core.dex.nodes.InsnNode;
|
import jadx.core.dex.nodes.InsnNode;
|
||||||
|
|
||||||
public class EdgeInsnAttr implements IAttribute {
|
public class EdgeInsnAttr implements IJadxAttribute {
|
||||||
|
|
||||||
private final BlockNode start;
|
private final BlockNode start;
|
||||||
private final BlockNode end;
|
private final BlockNode end;
|
||||||
@@ -31,7 +31,7 @@ public class EdgeInsnAttr implements IAttribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AType<AttrList<EdgeInsnAttr>> getType() {
|
public AType<AttrList<EdgeInsnAttr>> getAttrType() {
|
||||||
return AType.EDGE_INSN;
|
return AType.EDGE_INSN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,14 +2,14 @@ package jadx.core.dex.attributes.nodes;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import jadx.api.plugins.input.data.attributes.IJadxAttribute;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.IAttribute;
|
|
||||||
import jadx.core.dex.instructions.mods.ConstructorInsn;
|
import jadx.core.dex.instructions.mods.ConstructorInsn;
|
||||||
import jadx.core.dex.nodes.ClassNode;
|
import jadx.core.dex.nodes.ClassNode;
|
||||||
import jadx.core.dex.nodes.FieldNode;
|
import jadx.core.dex.nodes.FieldNode;
|
||||||
import jadx.core.dex.nodes.MethodNode;
|
import jadx.core.dex.nodes.MethodNode;
|
||||||
|
|
||||||
public class EnumClassAttr implements IAttribute {
|
public class EnumClassAttr implements IJadxAttribute {
|
||||||
|
|
||||||
public static class EnumField {
|
public static class EnumField {
|
||||||
private final FieldNode field;
|
private final FieldNode field;
|
||||||
@@ -63,7 +63,7 @@ public class EnumClassAttr implements IAttribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AType<EnumClassAttr> getType() {
|
public AType<EnumClassAttr> getAttrType() {
|
||||||
return AType.ENUM_CLASS;
|
return AType.ENUM_CLASS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,11 +5,11 @@ import java.util.Map;
|
|||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import jadx.api.plugins.input.data.attributes.IJadxAttribute;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.IAttribute;
|
|
||||||
import jadx.core.dex.nodes.FieldNode;
|
import jadx.core.dex.nodes.FieldNode;
|
||||||
|
|
||||||
public class EnumMapAttr implements IAttribute {
|
public class EnumMapAttr implements IJadxAttribute {
|
||||||
|
|
||||||
public static class KeyValueMap {
|
public static class KeyValueMap {
|
||||||
private final Map<Object, Object> map = new HashMap<>();
|
private final Map<Object, Object> map = new HashMap<>();
|
||||||
@@ -51,7 +51,7 @@ public class EnumMapAttr implements IAttribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AType<EnumMapAttr> getType() {
|
public AType<EnumMapAttr> getAttrType() {
|
||||||
return AType.ENUM_MAP;
|
return AType.ENUM_MAP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
package jadx.core.dex.attributes.nodes;
|
package jadx.core.dex.attributes.nodes;
|
||||||
|
|
||||||
|
import jadx.api.plugins.input.data.attributes.PinnedAttribute;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.IAttribute;
|
|
||||||
import jadx.core.dex.info.ClassInfo;
|
import jadx.core.dex.info.ClassInfo;
|
||||||
import jadx.core.dex.instructions.args.InsnArg;
|
import jadx.core.dex.instructions.args.InsnArg;
|
||||||
|
|
||||||
public class FieldReplaceAttr implements IAttribute {
|
public class FieldReplaceAttr extends PinnedAttribute {
|
||||||
|
|
||||||
public enum ReplaceWith {
|
public enum ReplaceWith {
|
||||||
CLASS_INSTANCE,
|
CLASS_INSTANCE,
|
||||||
@@ -38,7 +38,7 @@ public class FieldReplaceAttr implements IAttribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AType<FieldReplaceAttr> getType() {
|
public AType<FieldReplaceAttr> getAttrType() {
|
||||||
return AType.FIELD_REPLACE;
|
return AType.FIELD_REPLACE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
package jadx.core.dex.attributes.nodes;
|
package jadx.core.dex.attributes.nodes;
|
||||||
|
|
||||||
|
import jadx.api.plugins.input.data.attributes.IJadxAttribute;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.IAttribute;
|
|
||||||
import jadx.core.dex.nodes.InsnNode;
|
import jadx.core.dex.nodes.InsnNode;
|
||||||
import jadx.core.utils.Utils;
|
import jadx.core.utils.Utils;
|
||||||
|
|
||||||
public class ForceReturnAttr implements IAttribute {
|
public class ForceReturnAttr implements IJadxAttribute {
|
||||||
|
|
||||||
private final InsnNode returnInsn;
|
private final InsnNode returnInsn;
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ public class ForceReturnAttr implements IAttribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AType<ForceReturnAttr> getType() {
|
public AType<ForceReturnAttr> getAttrType() {
|
||||||
return AType.FORCE_RETURN;
|
return AType.FORCE_RETURN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ package jadx.core.dex.attributes.nodes;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import jadx.api.plugins.input.data.attributes.IJadxAttribute;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.IAttribute;
|
|
||||||
import jadx.core.dex.instructions.args.ArgType;
|
import jadx.core.dex.instructions.args.ArgType;
|
||||||
|
|
||||||
public class GenericInfoAttr implements IAttribute {
|
public class GenericInfoAttr implements IJadxAttribute {
|
||||||
private final List<ArgType> genericTypes;
|
private final List<ArgType> genericTypes;
|
||||||
private boolean explicit;
|
private boolean explicit;
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ public class GenericInfoAttr implements IAttribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AType<GenericInfoAttr> getType() {
|
public AType<GenericInfoAttr> getAttrType() {
|
||||||
return AType.GENERIC_INFO;
|
return AType.GENERIC_INFO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ package jadx.core.dex.attributes.nodes;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import jadx.api.plugins.input.data.attributes.IJadxAttribute;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.IAttribute;
|
|
||||||
import jadx.core.dex.nodes.BlockNode;
|
import jadx.core.dex.nodes.BlockNode;
|
||||||
import jadx.core.utils.Utils;
|
import jadx.core.utils.Utils;
|
||||||
|
|
||||||
public class IgnoreEdgeAttr implements IAttribute {
|
public class IgnoreEdgeAttr implements IJadxAttribute {
|
||||||
|
|
||||||
private final Set<BlockNode> blocks = new HashSet<>(3);
|
private final Set<BlockNode> blocks = new HashSet<>(3);
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ public class IgnoreEdgeAttr implements IAttribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AType<IgnoreEdgeAttr> getType() {
|
public AType<IgnoreEdgeAttr> getAttrType() {
|
||||||
return AType.IGNORE_EDGE;
|
return AType.IGNORE_EDGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,6 +35,12 @@ public abstract class LineAttrNode extends AttrNode {
|
|||||||
this.decompiledLine = decompiledLine;
|
this.decompiledLine = decompiledLine;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addSourceLineFrom(LineAttrNode lineAttrNode) {
|
||||||
|
if (this.getSourceLine() == 0) {
|
||||||
|
this.setSourceLine(lineAttrNode.getSourceLine());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void copyLines(LineAttrNode lineAttrNode) {
|
public void copyLines(LineAttrNode lineAttrNode) {
|
||||||
setSourceLine(lineAttrNode.getSourceLine());
|
setSourceLine(lineAttrNode.getSourceLine());
|
||||||
setDecompiledLine(lineAttrNode.getDecompiledLine());
|
setDecompiledLine(lineAttrNode.getDecompiledLine());
|
||||||
|
|||||||
@@ -4,11 +4,11 @@ import java.util.List;
|
|||||||
|
|
||||||
import jadx.api.ICodeWriter;
|
import jadx.api.ICodeWriter;
|
||||||
import jadx.api.plugins.input.data.ILocalVar;
|
import jadx.api.plugins.input.data.ILocalVar;
|
||||||
|
import jadx.api.plugins.input.data.attributes.IJadxAttribute;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.IAttribute;
|
|
||||||
import jadx.core.utils.Utils;
|
import jadx.core.utils.Utils;
|
||||||
|
|
||||||
public class LocalVarsDebugInfoAttr implements IAttribute {
|
public class LocalVarsDebugInfoAttr implements IJadxAttribute {
|
||||||
private final List<ILocalVar> localVars;
|
private final List<ILocalVar> localVars;
|
||||||
|
|
||||||
public LocalVarsDebugInfoAttr(List<ILocalVar> localVars) {
|
public LocalVarsDebugInfoAttr(List<ILocalVar> localVars) {
|
||||||
@@ -20,7 +20,7 @@ public class LocalVarsDebugInfoAttr implements IAttribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AType<LocalVarsDebugInfoAttr> getType() {
|
public AType<LocalVarsDebugInfoAttr> getAttrType() {
|
||||||
return AType.LOCAL_VARS_DEBUG_INFO;
|
return AType.LOCAL_VARS_DEBUG_INFO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package jadx.core.dex.attributes.nodes;
|
package jadx.core.dex.attributes.nodes;
|
||||||
|
|
||||||
|
import jadx.api.plugins.input.data.attributes.IJadxAttribute;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.IAttribute;
|
|
||||||
|
|
||||||
public class LoopLabelAttr implements IAttribute {
|
public class LoopLabelAttr implements IJadxAttribute {
|
||||||
|
|
||||||
private final LoopInfo loop;
|
private final LoopInfo loop;
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ public class LoopLabelAttr implements IAttribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AType<LoopLabelAttr> getType() {
|
public AType<LoopLabelAttr> getAttrType() {
|
||||||
return AType.LOOP_LABEL;
|
return AType.LOOP_LABEL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,15 +3,15 @@ package jadx.core.dex.attributes.nodes;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import jadx.api.plugins.input.data.attributes.PinnedAttribute;
|
||||||
import jadx.core.Consts;
|
import jadx.core.Consts;
|
||||||
import jadx.core.dex.attributes.AFlag;
|
import jadx.core.dex.attributes.AFlag;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.IAttribute;
|
|
||||||
import jadx.core.dex.instructions.args.RegisterArg;
|
import jadx.core.dex.instructions.args.RegisterArg;
|
||||||
import jadx.core.dex.nodes.InsnNode;
|
import jadx.core.dex.nodes.InsnNode;
|
||||||
import jadx.core.dex.nodes.MethodNode;
|
import jadx.core.dex.nodes.MethodNode;
|
||||||
|
|
||||||
public class MethodInlineAttr implements IAttribute {
|
public class MethodInlineAttr extends PinnedAttribute {
|
||||||
|
|
||||||
private static final MethodInlineAttr INLINE_NOT_NEEDED = new MethodInlineAttr(null, null);
|
private static final MethodInlineAttr INLINE_NOT_NEEDED = new MethodInlineAttr(null, null);
|
||||||
|
|
||||||
@@ -64,7 +64,7 @@ public class MethodInlineAttr implements IAttribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AType<MethodInlineAttr> getType() {
|
public AType<MethodInlineAttr> getAttrType() {
|
||||||
return AType.METHOD_INLINE;
|
return AType.METHOD_INLINE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,12 +3,12 @@ package jadx.core.dex.attributes.nodes;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
|
|
||||||
|
import jadx.api.plugins.input.data.attributes.PinnedAttribute;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.IAttribute;
|
|
||||||
import jadx.core.dex.nodes.IMethodDetails;
|
import jadx.core.dex.nodes.IMethodDetails;
|
||||||
import jadx.core.dex.nodes.MethodNode;
|
import jadx.core.dex.nodes.MethodNode;
|
||||||
|
|
||||||
public class MethodOverrideAttr implements IAttribute {
|
public class MethodOverrideAttr extends PinnedAttribute {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All methods overridden by current method. Current method excluded, empty for base method.
|
* All methods overridden by current method. Current method excluded, empty for base method.
|
||||||
@@ -46,7 +46,7 @@ public class MethodOverrideAttr implements IAttribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AType<MethodOverrideAttr> getType() {
|
public AType<MethodOverrideAttr> getAttrType() {
|
||||||
return AType.METHOD_OVERRIDE;
|
return AType.METHOD_OVERRIDE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ package jadx.core.dex.attributes.nodes;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import jadx.api.plugins.input.data.attributes.IJadxAttribute;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.IAttribute;
|
|
||||||
import jadx.core.dex.instructions.args.ArgType;
|
import jadx.core.dex.instructions.args.ArgType;
|
||||||
|
|
||||||
import static jadx.core.utils.Utils.isEmpty;
|
import static jadx.core.utils.Utils.isEmpty;
|
||||||
@@ -12,7 +12,7 @@ import static jadx.core.utils.Utils.isEmpty;
|
|||||||
/**
|
/**
|
||||||
* Set of known type variables at current method
|
* Set of known type variables at current method
|
||||||
*/
|
*/
|
||||||
public class MethodTypeVarsAttr implements IAttribute {
|
public class MethodTypeVarsAttr implements IJadxAttribute {
|
||||||
private static final MethodTypeVarsAttr EMPTY = new MethodTypeVarsAttr(Collections.emptySet());
|
private static final MethodTypeVarsAttr EMPTY = new MethodTypeVarsAttr(Collections.emptySet());
|
||||||
|
|
||||||
public static MethodTypeVarsAttr build(Set<ArgType> typeVars) {
|
public static MethodTypeVarsAttr build(Set<ArgType> typeVars) {
|
||||||
@@ -33,7 +33,7 @@ public class MethodTypeVarsAttr implements IAttribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AType<MethodTypeVarsAttr> getType() {
|
public AType<MethodTypeVarsAttr> getAttrType() {
|
||||||
return AType.METHOD_TYPE_VARS;
|
return AType.METHOD_TYPE_VARS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,16 +4,16 @@ import java.util.LinkedList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import jadx.api.ICodeWriter;
|
import jadx.api.ICodeWriter;
|
||||||
|
import jadx.api.plugins.input.data.attributes.IJadxAttribute;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.IAttribute;
|
|
||||||
import jadx.core.dex.instructions.PhiInsn;
|
import jadx.core.dex.instructions.PhiInsn;
|
||||||
|
|
||||||
public class PhiListAttr implements IAttribute {
|
public class PhiListAttr implements IJadxAttribute {
|
||||||
|
|
||||||
private final List<PhiInsn> list = new LinkedList<>();
|
private final List<PhiInsn> list = new LinkedList<>();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AType<PhiListAttr> getType() {
|
public AType<PhiListAttr> getAttrType() {
|
||||||
return AType.PHI_LIST;
|
return AType.PHI_LIST;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,11 +2,11 @@ package jadx.core.dex.attributes.nodes;
|
|||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
|
import jadx.api.plugins.input.data.attributes.IJadxAttribute;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.IAttribute;
|
|
||||||
import jadx.core.dex.instructions.args.ArgType;
|
import jadx.core.dex.instructions.args.ArgType;
|
||||||
|
|
||||||
public class RegDebugInfoAttr implements IAttribute {
|
public class RegDebugInfoAttr implements IJadxAttribute {
|
||||||
|
|
||||||
private final ArgType type;
|
private final ArgType type;
|
||||||
private final String name;
|
private final String name;
|
||||||
@@ -25,7 +25,7 @@ public class RegDebugInfoAttr implements IAttribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AType<RegDebugInfoAttr> getType() {
|
public AType<RegDebugInfoAttr> getAttrType() {
|
||||||
return AType.REG_DEBUG_INFO;
|
return AType.REG_DEBUG_INFO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
package jadx.core.dex.attributes.nodes;
|
package jadx.core.dex.attributes.nodes;
|
||||||
|
|
||||||
|
import jadx.api.plugins.input.data.attributes.IJadxAttribute;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.AttrNode;
|
import jadx.core.dex.attributes.AttrNode;
|
||||||
import jadx.core.dex.attributes.IAttribute;
|
|
||||||
|
|
||||||
public class RenameReasonAttr implements IAttribute {
|
public class RenameReasonAttr implements IJadxAttribute {
|
||||||
|
|
||||||
private String description;
|
private String description;
|
||||||
|
|
||||||
@@ -57,7 +57,7 @@ public class RenameReasonAttr implements IAttribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AType<RenameReasonAttr> getType() {
|
public AType<RenameReasonAttr> getAttrType() {
|
||||||
return AType.RENAME_REASON;
|
return AType.RENAME_REASON;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,14 +4,14 @@ import java.util.BitSet;
|
|||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import jadx.api.plugins.input.data.attributes.PinnedAttribute;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.IAttribute;
|
|
||||||
import jadx.core.dex.instructions.args.RegisterArg;
|
import jadx.core.dex.instructions.args.RegisterArg;
|
||||||
import jadx.core.dex.nodes.MethodNode;
|
import jadx.core.dex.nodes.MethodNode;
|
||||||
import jadx.core.utils.Utils;
|
import jadx.core.utils.Utils;
|
||||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||||
|
|
||||||
public class SkipMethodArgsAttr implements IAttribute {
|
public class SkipMethodArgsAttr extends PinnedAttribute {
|
||||||
|
|
||||||
public static void skipArg(MethodNode mth, RegisterArg arg) {
|
public static void skipArg(MethodNode mth, RegisterArg arg) {
|
||||||
int argNum = Utils.indexInListByRef(mth.getArgRegs(), arg);
|
int argNum = Utils.indexInListByRef(mth.getArgRegs(), arg);
|
||||||
@@ -60,7 +60,7 @@ public class SkipMethodArgsAttr implements IAttribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AType<SkipMethodArgsAttr> getType() {
|
public AType<SkipMethodArgsAttr> getAttrType() {
|
||||||
return AType.SKIP_MTH_ARGS;
|
return AType.SKIP_MTH_ARGS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,27 +0,0 @@
|
|||||||
package jadx.core.dex.attributes.nodes;
|
|
||||||
|
|
||||||
import jadx.core.dex.attributes.AType;
|
|
||||||
import jadx.core.dex.attributes.IAttribute;
|
|
||||||
|
|
||||||
public class SourceFileAttr implements IAttribute {
|
|
||||||
|
|
||||||
private final String fileName;
|
|
||||||
|
|
||||||
public SourceFileAttr(String fileName) {
|
|
||||||
this.fileName = fileName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getFileName() {
|
|
||||||
return fileName;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public AType<SourceFileAttr> getType() {
|
|
||||||
return AType.SOURCE_FILE;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "SOURCE:" + fileName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -51,6 +51,30 @@ public class AccessInfo {
|
|||||||
return new AccessInfo(accFlags & VISIBILITY_FLAGS, type);
|
return new AccessInfo(accFlags & VISIBILITY_FLAGS, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isVisibilityWeakerThan(AccessInfo otherAccInfo) {
|
||||||
|
int thisVis = accFlags & VISIBILITY_FLAGS;
|
||||||
|
int otherVis = otherAccInfo.accFlags & VISIBILITY_FLAGS;
|
||||||
|
if (thisVis == otherVis) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return orderedVisibility(thisVis) < orderedVisibility(otherVis);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int orderedVisibility(int flag) {
|
||||||
|
switch (flag) {
|
||||||
|
case AccessFlags.PRIVATE:
|
||||||
|
return 1;
|
||||||
|
case 0: // package-private
|
||||||
|
return 2;
|
||||||
|
case AccessFlags.PROTECTED:
|
||||||
|
return 3;
|
||||||
|
case AccessFlags.PUBLIC:
|
||||||
|
return 4;
|
||||||
|
default:
|
||||||
|
throw new JadxRuntimeException("Unexpected visibility flag: " + flag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isPublic() {
|
public boolean isPublic() {
|
||||||
return (accFlags & AccessFlags.PUBLIC) != 0;
|
return (accFlags & AccessFlags.PUBLIC) != 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ import java.util.concurrent.ConcurrentHashMap;
|
|||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import jadx.api.JadxArgs;
|
import jadx.api.JadxArgs;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.api.plugins.input.data.annotations.EncodedValue;
|
||||||
import jadx.core.dex.attributes.fldinit.FieldInitAttr;
|
import jadx.api.plugins.input.data.attributes.JadxAttrType;
|
||||||
import jadx.core.dex.instructions.args.LiteralArg;
|
import jadx.core.dex.instructions.args.LiteralArg;
|
||||||
import jadx.core.dex.instructions.args.PrimitiveType;
|
import jadx.core.dex.instructions.args.PrimitiveType;
|
||||||
import jadx.core.dex.nodes.ClassNode;
|
import jadx.core.dex.nodes.ClassNode;
|
||||||
@@ -84,9 +84,9 @@ public class ConstStorage {
|
|||||||
for (FieldNode f : staticFields) {
|
for (FieldNode f : staticFields) {
|
||||||
AccessInfo accFlags = f.getAccessFlags();
|
AccessInfo accFlags = f.getAccessFlags();
|
||||||
if (accFlags.isStatic() && accFlags.isFinal()) {
|
if (accFlags.isStatic() && accFlags.isFinal()) {
|
||||||
FieldInitAttr fv = f.get(AType.FIELD_INIT);
|
EncodedValue constVal = f.get(JadxAttrType.CONSTANT_VALUE);
|
||||||
if (fv != null && fv.isConst() && fv.getEncodedValue().getValue() != null) {
|
if (constVal != null && constVal.getValue() != null) {
|
||||||
addConstField(cls, f, fv.getEncodedValue().getValue(), accFlags.isPublic());
|
addConstField(cls, f, constVal.getValue(), accFlags.isPublic());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package jadx.core.dex.info;
|
|||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
import jadx.api.plugins.input.data.IFieldData;
|
import jadx.api.plugins.input.data.IFieldRef;
|
||||||
import jadx.core.codegen.TypeGen;
|
import jadx.core.codegen.TypeGen;
|
||||||
import jadx.core.dex.instructions.args.ArgType;
|
import jadx.core.dex.instructions.args.ArgType;
|
||||||
import jadx.core.dex.nodes.RootNode;
|
import jadx.core.dex.nodes.RootNode;
|
||||||
@@ -26,9 +26,9 @@ public final class FieldInfo {
|
|||||||
return root.getInfoStorage().getField(field);
|
return root.getInfoStorage().getField(field);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FieldInfo fromData(RootNode root, IFieldData fieldData) {
|
public static FieldInfo fromRef(RootNode root, IFieldRef fieldRef) {
|
||||||
ClassInfo declClass = ClassInfo.fromName(root, fieldData.getParentClassType());
|
ClassInfo declClass = ClassInfo.fromName(root, fieldRef.getParentClassType());
|
||||||
FieldInfo field = new FieldInfo(declClass, fieldData.getName(), ArgType.parse(fieldData.getType()));
|
FieldInfo field = new FieldInfo(declClass, fieldRef.getName(), ArgType.parse(fieldRef.getType()));
|
||||||
return root.getInfoStorage().getField(field);
|
return root.getInfoStorage().getField(field);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,16 @@
|
|||||||
package jadx.core.dex.instructions;
|
package jadx.core.dex.instructions;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import jadx.api.plugins.input.data.ICodeReader;
|
import jadx.api.plugins.input.data.ICodeReader;
|
||||||
|
import jadx.api.plugins.input.data.IMethodRef;
|
||||||
import jadx.api.plugins.input.insns.InsnData;
|
import jadx.api.plugins.input.insns.InsnData;
|
||||||
import jadx.api.plugins.input.insns.custom.IArrayPayload;
|
import jadx.api.plugins.input.insns.custom.IArrayPayload;
|
||||||
|
import jadx.api.plugins.input.insns.custom.ICustomPayload;
|
||||||
import jadx.api.plugins.input.insns.custom.ISwitchPayload;
|
import jadx.api.plugins.input.insns.custom.ISwitchPayload;
|
||||||
import jadx.core.Consts;
|
import jadx.core.Consts;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
@@ -35,7 +39,7 @@ public class InsnDecoder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public InsnNode[] process(ICodeReader codeReader) {
|
public InsnNode[] process(ICodeReader codeReader) {
|
||||||
InsnNode[] instructions = new InsnNode[codeReader.getInsnsCount()];
|
InsnNode[] instructions = new InsnNode[codeReader.getUnitsCount()];
|
||||||
codeReader.visitInstructions(rawInsn -> {
|
codeReader.visitInstructions(rawInsn -> {
|
||||||
int offset = rawInsn.getOffset();
|
int offset = rawInsn.getOffset();
|
||||||
InsnNode insn;
|
InsnNode insn;
|
||||||
@@ -88,6 +92,14 @@ public class InsnDecoder {
|
|||||||
InsnArg.reg(insn, 0, ArgType.NARROW),
|
InsnArg.reg(insn, 0, ArgType.NARROW),
|
||||||
InsnArg.reg(insn, 1, ArgType.NARROW));
|
InsnArg.reg(insn, 1, ArgType.NARROW));
|
||||||
|
|
||||||
|
case MOVE_MULTI:
|
||||||
|
int len = insn.getRegsCount();
|
||||||
|
InsnNode mmv = new InsnNode(InsnType.MOVE_MULTI, len);
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
mmv.addArg(InsnArg.reg(insn, i, ArgType.UNKNOWN));
|
||||||
|
}
|
||||||
|
return mmv;
|
||||||
|
|
||||||
case MOVE_WIDE:
|
case MOVE_WIDE:
|
||||||
return insn(InsnType.MOVE,
|
return insn(InsnType.MOVE,
|
||||||
InsnArg.reg(insn, 0, ArgType.WIDE),
|
InsnArg.reg(insn, 0, ArgType.WIDE),
|
||||||
@@ -339,31 +351,31 @@ public class InsnDecoder {
|
|||||||
ArgType castType = ArgType.parse(insn.getIndexAsType());
|
ArgType castType = ArgType.parse(insn.getIndexAsType());
|
||||||
InsnNode checkCastInsn = new IndexInsnNode(InsnType.CHECK_CAST, castType, 1);
|
InsnNode checkCastInsn = new IndexInsnNode(InsnType.CHECK_CAST, castType, 1);
|
||||||
checkCastInsn.setResult(InsnArg.reg(insn, 0, castType));
|
checkCastInsn.setResult(InsnArg.reg(insn, 0, castType));
|
||||||
checkCastInsn.addArg(InsnArg.reg(insn, 0, ArgType.UNKNOWN_OBJECT));
|
checkCastInsn.addArg(InsnArg.reg(insn, insn.getRegsCount() == 2 ? 1 : 0, ArgType.UNKNOWN_OBJECT));
|
||||||
return checkCastInsn;
|
return checkCastInsn;
|
||||||
|
|
||||||
case IGET:
|
case IGET:
|
||||||
FieldInfo igetFld = FieldInfo.fromData(root, insn.getIndexAsField());
|
FieldInfo igetFld = FieldInfo.fromRef(root, insn.getIndexAsField());
|
||||||
InsnNode igetInsn = new IndexInsnNode(InsnType.IGET, igetFld, 1);
|
InsnNode igetInsn = new IndexInsnNode(InsnType.IGET, igetFld, 1);
|
||||||
igetInsn.setResult(InsnArg.reg(insn, 0, tryResolveFieldType(igetFld)));
|
igetInsn.setResult(InsnArg.reg(insn, 0, tryResolveFieldType(igetFld)));
|
||||||
igetInsn.addArg(InsnArg.reg(insn, 1, igetFld.getDeclClass().getType()));
|
igetInsn.addArg(InsnArg.reg(insn, 1, igetFld.getDeclClass().getType()));
|
||||||
return igetInsn;
|
return igetInsn;
|
||||||
|
|
||||||
case IPUT:
|
case IPUT:
|
||||||
FieldInfo iputFld = FieldInfo.fromData(root, insn.getIndexAsField());
|
FieldInfo iputFld = FieldInfo.fromRef(root, insn.getIndexAsField());
|
||||||
InsnNode iputInsn = new IndexInsnNode(InsnType.IPUT, iputFld, 2);
|
InsnNode iputInsn = new IndexInsnNode(InsnType.IPUT, iputFld, 2);
|
||||||
iputInsn.addArg(InsnArg.reg(insn, 0, tryResolveFieldType(iputFld)));
|
iputInsn.addArg(InsnArg.reg(insn, 0, tryResolveFieldType(iputFld)));
|
||||||
iputInsn.addArg(InsnArg.reg(insn, 1, iputFld.getDeclClass().getType()));
|
iputInsn.addArg(InsnArg.reg(insn, 1, iputFld.getDeclClass().getType()));
|
||||||
return iputInsn;
|
return iputInsn;
|
||||||
|
|
||||||
case SGET:
|
case SGET:
|
||||||
FieldInfo sgetFld = FieldInfo.fromData(root, insn.getIndexAsField());
|
FieldInfo sgetFld = FieldInfo.fromRef(root, insn.getIndexAsField());
|
||||||
InsnNode sgetInsn = new IndexInsnNode(InsnType.SGET, sgetFld, 0);
|
InsnNode sgetInsn = new IndexInsnNode(InsnType.SGET, sgetFld, 0);
|
||||||
sgetInsn.setResult(InsnArg.reg(insn, 0, tryResolveFieldType(sgetFld)));
|
sgetInsn.setResult(InsnArg.reg(insn, 0, tryResolveFieldType(sgetFld)));
|
||||||
return sgetInsn;
|
return sgetInsn;
|
||||||
|
|
||||||
case SPUT:
|
case SPUT:
|
||||||
FieldInfo sputFld = FieldInfo.fromData(root, insn.getIndexAsField());
|
FieldInfo sputFld = FieldInfo.fromRef(root, insn.getIndexAsField());
|
||||||
InsnNode sputInsn = new IndexInsnNode(InsnType.SPUT, sputFld, 1);
|
InsnNode sputInsn = new IndexInsnNode(InsnType.SPUT, sputFld, 1);
|
||||||
sputInsn.addArg(InsnArg.reg(insn, 0, tryResolveFieldType(sputFld)));
|
sputInsn.addArg(InsnArg.reg(insn, 0, tryResolveFieldType(sputFld)));
|
||||||
return sputInsn;
|
return sputInsn;
|
||||||
@@ -380,6 +392,8 @@ public class InsnDecoder {
|
|||||||
return arrayGet(insn, ArgType.BOOLEAN);
|
return arrayGet(insn, ArgType.BOOLEAN);
|
||||||
case AGET_BYTE:
|
case AGET_BYTE:
|
||||||
return arrayGet(insn, ArgType.BYTE);
|
return arrayGet(insn, ArgType.BYTE);
|
||||||
|
case AGET_BYTE_BOOLEAN:
|
||||||
|
return arrayGet(insn, ArgType.BYTE_BOOLEAN);
|
||||||
case AGET_CHAR:
|
case AGET_CHAR:
|
||||||
return arrayGet(insn, ArgType.CHAR);
|
return arrayGet(insn, ArgType.CHAR);
|
||||||
case AGET_SHORT:
|
case AGET_SHORT:
|
||||||
@@ -395,6 +409,8 @@ public class InsnDecoder {
|
|||||||
return arrayPut(insn, ArgType.BOOLEAN);
|
return arrayPut(insn, ArgType.BOOLEAN);
|
||||||
case APUT_BYTE:
|
case APUT_BYTE:
|
||||||
return arrayPut(insn, ArgType.BYTE);
|
return arrayPut(insn, ArgType.BYTE);
|
||||||
|
case APUT_BYTE_BOOLEAN:
|
||||||
|
return arrayPut(insn, ArgType.BYTE_BOOLEAN);
|
||||||
case APUT_CHAR:
|
case APUT_CHAR:
|
||||||
return arrayPut(insn, ArgType.CHAR);
|
return arrayPut(insn, ArgType.CHAR);
|
||||||
case APUT_SHORT:
|
case APUT_SHORT:
|
||||||
@@ -439,15 +455,12 @@ public class InsnDecoder {
|
|||||||
return newInstInsn;
|
return newInstInsn;
|
||||||
|
|
||||||
case NEW_ARRAY:
|
case NEW_ARRAY:
|
||||||
ArgType arrType = ArgType.parse(insn.getIndexAsType());
|
return makeNewArray(insn);
|
||||||
return new NewArrayNode(arrType,
|
|
||||||
InsnArg.reg(insn, 0, arrType),
|
|
||||||
InsnArg.typeImmutableReg(insn, 1, ArgType.INT));
|
|
||||||
|
|
||||||
case FILL_ARRAY_DATA:
|
case FILL_ARRAY_DATA:
|
||||||
return new FillArrayInsn(InsnArg.reg(insn, 0, ArgType.UNKNOWN_ARRAY), insn.getTarget());
|
return new FillArrayInsn(InsnArg.reg(insn, 0, ArgType.UNKNOWN_ARRAY), insn.getTarget());
|
||||||
case FILL_ARRAY_DATA_PAYLOAD:
|
case FILL_ARRAY_DATA_PAYLOAD:
|
||||||
return new FillArrayData(((IArrayPayload) insn.getPayload()));
|
return new FillArrayData(((IArrayPayload) Objects.requireNonNull(insn.getPayload())));
|
||||||
|
|
||||||
case FILLED_NEW_ARRAY:
|
case FILLED_NEW_ARRAY:
|
||||||
return filledNewArray(insn, false);
|
return filledNewArray(insn, false);
|
||||||
@@ -455,9 +468,9 @@ public class InsnDecoder {
|
|||||||
return filledNewArray(insn, true);
|
return filledNewArray(insn, true);
|
||||||
|
|
||||||
case PACKED_SWITCH:
|
case PACKED_SWITCH:
|
||||||
return new SwitchInsn(InsnArg.reg(insn, 0, ArgType.UNKNOWN), insn.getTarget(), true);
|
return makeSwitch(insn, true);
|
||||||
case SPARSE_SWITCH:
|
case SPARSE_SWITCH:
|
||||||
return new SwitchInsn(InsnArg.reg(insn, 0, ArgType.UNKNOWN), insn.getTarget(), false);
|
return makeSwitch(insn, false);
|
||||||
|
|
||||||
case PACKED_SWITCH_PAYLOAD:
|
case PACKED_SWITCH_PAYLOAD:
|
||||||
case SPARSE_SWITCH_PAYLOAD:
|
case SPARSE_SWITCH_PAYLOAD:
|
||||||
@@ -478,6 +491,29 @@ public class InsnDecoder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private SwitchInsn makeSwitch(InsnData insn, boolean packed) {
|
||||||
|
SwitchInsn swInsn = new SwitchInsn(InsnArg.reg(insn, 0, ArgType.UNKNOWN), insn.getTarget(), packed);
|
||||||
|
ICustomPayload payload = insn.getPayload();
|
||||||
|
if (payload != null) {
|
||||||
|
swInsn.attachSwitchData(new SwitchData((ISwitchPayload) payload), insn.getTarget());
|
||||||
|
}
|
||||||
|
return swInsn;
|
||||||
|
}
|
||||||
|
|
||||||
|
private InsnNode makeNewArray(InsnData insn) {
|
||||||
|
ArgType indexType = ArgType.parse(insn.getIndexAsType());
|
||||||
|
int dim = (int) insn.getLiteral();
|
||||||
|
ArgType arrType = dim == 0 ? indexType : ArgType.array(indexType, dim);
|
||||||
|
int regsCount = insn.getRegsCount();
|
||||||
|
NewArrayNode newArr = new NewArrayNode(arrType, regsCount - 1);
|
||||||
|
newArr.setResult(InsnArg.reg(insn, 0, arrType));
|
||||||
|
for (int i = 1; i < regsCount; i++) {
|
||||||
|
newArr.addArg(InsnArg.typeImmutableReg(insn, i, ArgType.INT));
|
||||||
|
}
|
||||||
|
return newArr;
|
||||||
|
}
|
||||||
|
|
||||||
private ArgType tryResolveFieldType(FieldInfo igetFld) {
|
private ArgType tryResolveFieldType(FieldInfo igetFld) {
|
||||||
FieldNode fieldNode = root.resolveField(igetFld);
|
FieldNode fieldNode = root.resolveField(igetFld);
|
||||||
if (fieldNode != null) {
|
if (fieldNode != null) {
|
||||||
@@ -531,7 +567,14 @@ public class InsnDecoder {
|
|||||||
if (type == InvokeType.CUSTOM) {
|
if (type == InvokeType.CUSTOM) {
|
||||||
return InvokeCustomBuilder.build(method, insn, isRange);
|
return InvokeCustomBuilder.build(method, insn, isRange);
|
||||||
}
|
}
|
||||||
MethodInfo mthInfo = MethodInfo.fromRef(root, insn.getIndexAsMethod());
|
IMethodRef mthRef;
|
||||||
|
ICustomPayload payload = insn.getPayload();
|
||||||
|
if (payload != null) {
|
||||||
|
mthRef = ((IMethodRef) payload);
|
||||||
|
} else {
|
||||||
|
mthRef = insn.getIndexAsMethod();
|
||||||
|
}
|
||||||
|
MethodInfo mthInfo = MethodInfo.fromRef(root, mthRef);
|
||||||
return new InvokeNode(mthInfo, insn, type, isRange);
|
return new InvokeNode(mthInfo, insn, type, isRange);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ public enum InsnType {
|
|||||||
NOT,
|
NOT,
|
||||||
|
|
||||||
MOVE,
|
MOVE,
|
||||||
|
MOVE_MULTI,
|
||||||
CAST,
|
CAST,
|
||||||
|
|
||||||
RETURN,
|
RETURN,
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import jadx.api.plugins.input.data.IMethodRef;
|
|||||||
import jadx.api.plugins.input.data.MethodHandleType;
|
import jadx.api.plugins.input.data.MethodHandleType;
|
||||||
import jadx.api.plugins.input.data.annotations.EncodedValue;
|
import jadx.api.plugins.input.data.annotations.EncodedValue;
|
||||||
import jadx.api.plugins.input.insns.InsnData;
|
import jadx.api.plugins.input.insns.InsnData;
|
||||||
|
import jadx.api.plugins.input.insns.custom.ICustomPayload;
|
||||||
import jadx.core.dex.attributes.AFlag;
|
import jadx.core.dex.attributes.AFlag;
|
||||||
import jadx.core.dex.info.ClassInfo;
|
import jadx.core.dex.info.ClassInfo;
|
||||||
import jadx.core.dex.info.MethodInfo;
|
import jadx.core.dex.info.MethodInfo;
|
||||||
@@ -28,7 +29,13 @@ public class InvokeCustomBuilder {
|
|||||||
|
|
||||||
public static InsnNode build(MethodNode mth, InsnData insn, boolean isRange) {
|
public static InsnNode build(MethodNode mth, InsnData insn, boolean isRange) {
|
||||||
try {
|
try {
|
||||||
ICallSite callSite = insn.getIndexAsCallSite();
|
ICallSite callSite;
|
||||||
|
ICustomPayload payload = insn.getPayload();
|
||||||
|
if (payload != null) {
|
||||||
|
callSite = (ICallSite) payload;
|
||||||
|
} else {
|
||||||
|
callSite = insn.getIndexAsCallSite();
|
||||||
|
}
|
||||||
callSite.load();
|
callSite.load();
|
||||||
List<EncodedValue> values = callSite.getValues();
|
List<EncodedValue> values = callSite.getValues();
|
||||||
if (!checkLinkerMethod(values)) {
|
if (!checkLinkerMethod(values)) {
|
||||||
@@ -38,7 +45,12 @@ public class InvokeCustomBuilder {
|
|||||||
if (callMthHandle.getType().isField()) {
|
if (callMthHandle.getType().isField()) {
|
||||||
throw new JadxRuntimeException("Not yet supported");
|
throw new JadxRuntimeException("Not yet supported");
|
||||||
}
|
}
|
||||||
return buildMethodCall(mth, insn, isRange, values, callMthHandle);
|
InvokeCustomNode resNode = buildMethodCall(mth, insn, isRange, values, callMthHandle);
|
||||||
|
int resReg = insn.getResultReg();
|
||||||
|
if (resReg != -1) {
|
||||||
|
resNode.setResult(InsnArg.reg(resReg, mth.getReturnType()));
|
||||||
|
}
|
||||||
|
return resNode;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new JadxRuntimeException("'invoke-custom' instruction processing error: " + e.getMessage(), e);
|
throw new JadxRuntimeException("'invoke-custom' instruction processing error: " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
@@ -75,7 +87,6 @@ public class InvokeCustomBuilder {
|
|||||||
if (callMth != null) {
|
if (callMth != null) {
|
||||||
invokeCustomNode.getCallInsn().addAttr(callMth);
|
invokeCustomNode.getCallInsn().addAttr(callMth);
|
||||||
if (callMth.getAccessFlags().isSynthetic()
|
if (callMth.getAccessFlags().isSynthetic()
|
||||||
&& callMth.getUseIn().size() <= 1
|
|
||||||
&& callMth.getParentClass().equals(mth.getParentClass())) {
|
&& callMth.getParentClass().equals(mth.getParentClass())) {
|
||||||
// inline only synthetic methods from same class
|
// inline only synthetic methods from same class
|
||||||
callMth.add(AFlag.DONT_GENERATE);
|
callMth.add(AFlag.DONT_GENERATE);
|
||||||
|
|||||||
@@ -28,11 +28,14 @@ public class InvokeNode extends BaseInvokeNode {
|
|||||||
addReg(r, mth.getDeclClass().getType());
|
addReg(r, mth.getDeclClass().getType());
|
||||||
k++;
|
k++;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ArgType arg : mth.getArgumentsTypes()) {
|
for (ArgType arg : mth.getArgumentsTypes()) {
|
||||||
addReg(isRange ? k : insn.getReg(k), arg);
|
addReg(isRange ? k : insn.getReg(k), arg);
|
||||||
k += arg.getRegCount();
|
k += arg.getRegCount();
|
||||||
}
|
}
|
||||||
|
int resReg = insn.getResultReg();
|
||||||
|
if (resReg != -1) {
|
||||||
|
setResult(InsnArg.reg(resReg, mth.getReturnType()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public InvokeNode(MethodInfo mth, InvokeType invokeType, int argsCount) {
|
public InvokeNode(MethodInfo mth, InvokeType invokeType, int argsCount) {
|
||||||
|
|||||||
@@ -1,24 +1,14 @@
|
|||||||
package jadx.core.dex.instructions;
|
package jadx.core.dex.instructions;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import jadx.core.dex.instructions.args.ArgType;
|
import jadx.core.dex.instructions.args.ArgType;
|
||||||
import jadx.core.dex.instructions.args.InsnArg;
|
|
||||||
import jadx.core.dex.instructions.args.RegisterArg;
|
|
||||||
import jadx.core.dex.nodes.InsnNode;
|
import jadx.core.dex.nodes.InsnNode;
|
||||||
|
|
||||||
public class NewArrayNode extends InsnNode {
|
public class NewArrayNode extends InsnNode {
|
||||||
|
|
||||||
private final ArgType arrType;
|
private final ArgType arrType;
|
||||||
|
|
||||||
public NewArrayNode(@NotNull ArgType arrType, RegisterArg res, InsnArg size) {
|
public NewArrayNode(ArgType arrType, int argsCount) {
|
||||||
this(arrType);
|
super(InsnType.NEW_ARRAY, argsCount);
|
||||||
setResult(res);
|
|
||||||
addArg(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
private NewArrayNode(ArgType arrType) {
|
|
||||||
super(InsnType.NEW_ARRAY, 1);
|
|
||||||
this.arrType = arrType;
|
this.arrType = arrType;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,6 +16,10 @@ public class NewArrayNode extends InsnNode {
|
|||||||
return arrType;
|
return arrType;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getDimension() {
|
||||||
|
return arrType.getArrayDimension();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isSame(InsnNode obj) {
|
public boolean isSame(InsnNode obj) {
|
||||||
if (this == obj) {
|
if (this == obj) {
|
||||||
@@ -40,7 +34,7 @@ public class NewArrayNode extends InsnNode {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public InsnNode copy() {
|
public InsnNode copy() {
|
||||||
return copyCommonParams(new NewArrayNode(arrType));
|
return copyCommonParams(new NewArrayNode(arrType, getArgsCount()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -16,6 +16,14 @@ public class SwitchData extends InsnNode {
|
|||||||
this.targets = payload.getTargets();
|
this.targets = payload.getTargets();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void fixTargets(int switchOffset) {
|
||||||
|
int size = this.size;
|
||||||
|
int[] targets = this.targets;
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
targets[i] += switchOffset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public int getSize() {
|
public int getSize() {
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,16 +33,13 @@ public class SwitchInsn extends TargetInsnNode {
|
|||||||
this.packed = packed;
|
this.packed = packed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean needData() {
|
||||||
|
return this.switchData == null;
|
||||||
|
}
|
||||||
|
|
||||||
public void attachSwitchData(SwitchData data, int def) {
|
public void attachSwitchData(SwitchData data, int def) {
|
||||||
this.switchData = data;
|
this.switchData = data;
|
||||||
this.def = def;
|
this.def = def;
|
||||||
// fix targets
|
|
||||||
int switchOffset = getOffset();
|
|
||||||
int size = data.getSize();
|
|
||||||
int[] targets = data.getTargets();
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
targets[i] += switchOffset;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ public abstract class ArgType {
|
|||||||
|
|
||||||
public static final ArgType INT_FLOAT = unknown(PrimitiveType.INT, PrimitiveType.FLOAT);
|
public static final ArgType INT_FLOAT = unknown(PrimitiveType.INT, PrimitiveType.FLOAT);
|
||||||
public static final ArgType INT_BOOLEAN = unknown(PrimitiveType.INT, PrimitiveType.BOOLEAN);
|
public static final ArgType INT_BOOLEAN = unknown(PrimitiveType.INT, PrimitiveType.BOOLEAN);
|
||||||
|
public static final ArgType BYTE_BOOLEAN = unknown(PrimitiveType.BYTE, PrimitiveType.BOOLEAN);
|
||||||
|
|
||||||
protected int hash;
|
protected int hash;
|
||||||
|
|
||||||
@@ -149,6 +150,17 @@ public abstract class ArgType {
|
|||||||
return new ArrayArg(vtype);
|
return new ArrayArg(vtype);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ArgType array(@NotNull ArgType type, int dimension) {
|
||||||
|
if (dimension == 1) {
|
||||||
|
return new ArrayArg(type);
|
||||||
|
}
|
||||||
|
ArgType arrType = type;
|
||||||
|
for (int i = 0; i < dimension; i++) {
|
||||||
|
arrType = new ArrayArg(arrType);
|
||||||
|
}
|
||||||
|
return arrType;
|
||||||
|
}
|
||||||
|
|
||||||
public static ArgType unknown(PrimitiveType... types) {
|
public static ArgType unknown(PrimitiveType... types) {
|
||||||
return new UnknownArg(types);
|
return new UnknownArg(types);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package jadx.core.dex.instructions.mods;
|
package jadx.core.dex.instructions.mods;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import jadx.core.dex.instructions.InsnType;
|
import jadx.core.dex.instructions.InsnType;
|
||||||
import jadx.core.dex.instructions.args.InsnArg;
|
import jadx.core.dex.instructions.args.InsnArg;
|
||||||
@@ -27,6 +28,7 @@ public final class TernaryInsn extends InsnNode {
|
|||||||
addArg(th);
|
addArg(th);
|
||||||
addArg(els);
|
addArg(els);
|
||||||
}
|
}
|
||||||
|
visitInsns(this::inheritMetadata);
|
||||||
}
|
}
|
||||||
|
|
||||||
private TernaryInsn() {
|
private TernaryInsn() {
|
||||||
@@ -57,6 +59,11 @@ public final class TernaryInsn extends InsnNode {
|
|||||||
list.addAll(condition.getRegisterArgs());
|
list.addAll(condition.getRegisterArgs());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void visitInsns(Consumer<InsnNode> visitor) {
|
||||||
|
super.visitInsns(visitor);
|
||||||
|
condition.visitInsns(visitor);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isSame(InsnNode obj) {
|
public boolean isSame(InsnNode obj) {
|
||||||
if (this == obj) {
|
if (this == obj) {
|
||||||
|
|||||||
@@ -18,17 +18,19 @@ import org.slf4j.LoggerFactory;
|
|||||||
|
|
||||||
import jadx.api.ICodeCache;
|
import jadx.api.ICodeCache;
|
||||||
import jadx.api.ICodeInfo;
|
import jadx.api.ICodeInfo;
|
||||||
|
import jadx.api.ICodeWriter;
|
||||||
import jadx.api.plugins.input.data.IClassData;
|
import jadx.api.plugins.input.data.IClassData;
|
||||||
import jadx.api.plugins.input.data.annotations.EncodedValue;
|
import jadx.api.plugins.input.data.annotations.EncodedValue;
|
||||||
import jadx.api.plugins.input.data.annotations.IAnnotation;
|
import jadx.api.plugins.input.data.attributes.JadxAttrType;
|
||||||
|
import jadx.api.plugins.input.data.attributes.types.AnnotationDefaultAttr;
|
||||||
|
import jadx.api.plugins.input.data.attributes.types.AnnotationDefaultClassAttr;
|
||||||
|
import jadx.api.plugins.input.data.attributes.types.InnerClassesAttr;
|
||||||
|
import jadx.api.plugins.input.data.attributes.types.InnerClsInfo;
|
||||||
|
import jadx.api.plugins.input.data.attributes.types.SourceFileAttr;
|
||||||
import jadx.core.Consts;
|
import jadx.core.Consts;
|
||||||
import jadx.core.ProcessClass;
|
import jadx.core.ProcessClass;
|
||||||
import jadx.core.dex.attributes.AFlag;
|
import jadx.core.dex.attributes.AFlag;
|
||||||
import jadx.core.dex.attributes.annotations.AnnotationsList;
|
|
||||||
import jadx.core.dex.attributes.fldinit.FieldInitAttr;
|
|
||||||
import jadx.core.dex.attributes.fldinit.FieldInitConstAttr;
|
|
||||||
import jadx.core.dex.attributes.nodes.NotificationAttrNode;
|
import jadx.core.dex.attributes.nodes.NotificationAttrNode;
|
||||||
import jadx.core.dex.attributes.nodes.SourceFileAttr;
|
|
||||||
import jadx.core.dex.info.AccessInfo;
|
import jadx.core.dex.info.AccessInfo;
|
||||||
import jadx.core.dex.info.AccessInfo.AFType;
|
import jadx.core.dex.info.AccessInfo.AFType;
|
||||||
import jadx.core.dex.info.ClassInfo;
|
import jadx.core.dex.info.ClassInfo;
|
||||||
@@ -113,11 +115,10 @@ public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeN
|
|||||||
fld -> fields.add(FieldNode.build(this, fld)),
|
fld -> fields.add(FieldNode.build(this, fld)),
|
||||||
mth -> methods.add(MethodNode.build(this, mth)));
|
mth -> methods.add(MethodNode.build(this, mth)));
|
||||||
|
|
||||||
AnnotationsList.attach(this, cls.getAnnotations());
|
addAttrs(cls.getAttributes());
|
||||||
loadStaticValues(cls, fields);
|
accessFlags = new AccessInfo(getAccessFlags(cls), AFType.CLASS);
|
||||||
initAccessFlags(cls);
|
initStaticValues(fields);
|
||||||
|
processAttributes(this);
|
||||||
addSourceFilenameAttr(cls.getSourceFile());
|
|
||||||
buildCache();
|
buildCache();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new JadxRuntimeException("Error decode class: " + clsInfo, e);
|
throw new JadxRuntimeException("Error decode class: " + clsInfo, e);
|
||||||
@@ -130,18 +131,36 @@ public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeN
|
|||||||
this.generics = generics;
|
this.generics = generics;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private static void processAttributes(ClassNode cls) {
|
||||||
* Restore original access flags from Dalvik annotation if present
|
// move AnnotationDefault from cls to methods (dex specific)
|
||||||
*/
|
AnnotationDefaultClassAttr defAttr = cls.get(JadxAttrType.ANNOTATION_DEFAULT_CLASS);
|
||||||
private void initAccessFlags(IClassData cls) {
|
if (defAttr != null) {
|
||||||
int accFlagsValue;
|
cls.remove(JadxAttrType.ANNOTATION_DEFAULT_CLASS);
|
||||||
IAnnotation a = getAnnotation(Consts.DALVIK_INNER_CLASS);
|
for (Map.Entry<String, EncodedValue> entry : defAttr.getValues().entrySet()) {
|
||||||
if (a != null) {
|
MethodNode mth = cls.searchMethodByShortName(entry.getKey());
|
||||||
accFlagsValue = (Integer) a.getValues().get("accessFlags").getValue();
|
if (mth != null) {
|
||||||
} else {
|
mth.addAttr(new AnnotationDefaultAttr(entry.getValue()));
|
||||||
accFlagsValue = cls.getAccessFlags();
|
} else {
|
||||||
|
cls.addWarnComment("Method from annotation default annotation not found: " + entry.getKey());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
this.accessFlags = new AccessInfo(accFlagsValue, AFType.CLASS);
|
|
||||||
|
// check source file attribute
|
||||||
|
if (!cls.checkSourceFilenameAttr()) {
|
||||||
|
cls.remove(JadxAttrType.SOURCE_FILE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getAccessFlags(IClassData cls) {
|
||||||
|
InnerClassesAttr innerClassesAttr = get(JadxAttrType.INNER_CLASSES);
|
||||||
|
if (innerClassesAttr != null) {
|
||||||
|
InnerClsInfo innerClsInfo = innerClassesAttr.getMap().get(cls.getType());
|
||||||
|
if (innerClsInfo != null) {
|
||||||
|
return innerClsInfo.getAccessFlags();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cls.getAccessFlags();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ClassNode addSyntheticClass(RootNode root, String name, int accessFlags) {
|
public static ClassNode addSyntheticClass(RootNode root, String name, int accessFlags) {
|
||||||
@@ -164,26 +183,18 @@ public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeN
|
|||||||
this.parentClass = this;
|
this.parentClass = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadStaticValues(IClassData cls, List<FieldNode> fields) {
|
private void initStaticValues(List<FieldNode> fields) {
|
||||||
if (fields.isEmpty()) {
|
if (fields.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
List<FieldNode> staticFields = fields.stream().filter(FieldNode::isStatic).collect(Collectors.toList());
|
List<FieldNode> staticFields = fields.stream().filter(FieldNode::isStatic).collect(Collectors.toList());
|
||||||
for (FieldNode f : staticFields) {
|
for (FieldNode f : staticFields) {
|
||||||
if (f.getAccessFlags().isFinal()) {
|
if (f.getAccessFlags().isFinal() && f.get(JadxAttrType.CONSTANT_VALUE) == null) {
|
||||||
// incorrect initialization will be removed if assign found in constructor
|
// incorrect initialization will be removed if assign found in constructor
|
||||||
f.addAttr(FieldInitConstAttr.NULL_VALUE);
|
f.addAttr(EncodedValue.NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
List<EncodedValue> values = cls.getStaticFieldInitValues();
|
|
||||||
int count = values.size();
|
|
||||||
if (count == 0 || count > staticFields.size()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
staticFields.get(i).addAttr(FieldInitAttr.constValue(values.get(i)));
|
|
||||||
}
|
|
||||||
// process const fields
|
// process const fields
|
||||||
root().getConstValues().processConstFields(this, staticFields);
|
root().getConstValues().processConstFields(this, staticFields);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -191,26 +202,39 @@ public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeN
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addSourceFilenameAttr(String fileName) {
|
private boolean checkSourceFilenameAttr() {
|
||||||
if (fileName == null) {
|
SourceFileAttr sourceFileAttr = get(JadxAttrType.SOURCE_FILE);
|
||||||
return;
|
if (sourceFileAttr == null) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
String fileName = sourceFileAttr.getFileName();
|
||||||
if (fileName.endsWith(".java")) {
|
if (fileName.endsWith(".java")) {
|
||||||
fileName = fileName.substring(0, fileName.length() - 5);
|
fileName = fileName.substring(0, fileName.length() - 5);
|
||||||
}
|
}
|
||||||
if (fileName.isEmpty() || fileName.equals("SourceFile")) {
|
if (fileName.isEmpty() || fileName.equals("SourceFile")) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
if (clsInfo != null) {
|
if (clsInfo != null) {
|
||||||
String name = clsInfo.getShortName();
|
String name = clsInfo.getShortName();
|
||||||
if (fileName.equals(name)) {
|
if (fileName.equals(name)) {
|
||||||
return;
|
return false;
|
||||||
|
}
|
||||||
|
ClassInfo parentCls = clsInfo.getParentClass();
|
||||||
|
while (parentCls != null) {
|
||||||
|
String parentName = parentCls.getShortName();
|
||||||
|
if (parentName.equals(fileName) || parentName.startsWith(fileName + '$')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
parentCls = parentCls.getParentClass();
|
||||||
}
|
}
|
||||||
if (fileName.contains("$") && fileName.endsWith('$' + name)) {
|
if (fileName.contains("$") && fileName.endsWith('$' + name)) {
|
||||||
return;
|
return false;
|
||||||
|
}
|
||||||
|
if (name.contains("$") && name.startsWith(fileName)) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
this.addAttr(new SourceFileAttr(fileName));
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ensureProcessed() {
|
public void ensureProcessed() {
|
||||||
@@ -570,30 +594,30 @@ public class ClassNode extends NotificationAttrNode implements ILoadable, ICodeN
|
|||||||
return clsInfo.getAliasPkg();
|
return clsInfo.getAliasPkg();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getSmali() {
|
public String getDisassembledCode() {
|
||||||
if (smali == null) {
|
if (smali == null) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
getSmali(sb);
|
getDisassembledCode(sb);
|
||||||
sb.append(System.lineSeparator());
|
sb.append(ICodeWriter.NL);
|
||||||
Set<ClassNode> allInlinedClasses = new LinkedHashSet<>();
|
Set<ClassNode> allInlinedClasses = new LinkedHashSet<>();
|
||||||
getInnerAndInlinedClassesRecursive(allInlinedClasses);
|
getInnerAndInlinedClassesRecursive(allInlinedClasses);
|
||||||
for (ClassNode innerClass : allInlinedClasses) {
|
for (ClassNode innerClass : allInlinedClasses) {
|
||||||
innerClass.getSmali(sb);
|
innerClass.getDisassembledCode(sb);
|
||||||
sb.append(System.lineSeparator());
|
sb.append(ICodeWriter.NL);
|
||||||
}
|
}
|
||||||
smali = sb.toString();
|
smali = sb.toString();
|
||||||
}
|
}
|
||||||
return smali;
|
return smali;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void getSmali(StringBuilder sb) {
|
protected void getDisassembledCode(StringBuilder sb) {
|
||||||
if (this.clsData == null) {
|
if (clsData == null) {
|
||||||
sb.append(String.format("###### Class %s is created by jadx", getFullName()));
|
sb.append(String.format("###### Class %s is created by jadx", getFullName()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
sb.append(String.format("###### Class %s (%s)", getFullName(), getRawName()));
|
sb.append(String.format("###### Class %s (%s)", getFullName(), getRawName()));
|
||||||
sb.append(System.lineSeparator());
|
sb.append(ICodeWriter.NL);
|
||||||
sb.append(this.clsData.getDisassembledCode());
|
sb.append(clsData.getDisassembledCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
public IClassData getClsData() {
|
public IClassData getClsData() {
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import java.util.Collections;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import jadx.api.plugins.input.data.IFieldData;
|
import jadx.api.plugins.input.data.IFieldData;
|
||||||
import jadx.core.dex.attributes.annotations.AnnotationsList;
|
|
||||||
import jadx.core.dex.attributes.nodes.LineAttrNode;
|
import jadx.core.dex.attributes.nodes.LineAttrNode;
|
||||||
import jadx.core.dex.info.AccessInfo;
|
import jadx.core.dex.info.AccessInfo;
|
||||||
import jadx.core.dex.info.AccessInfo.AFType;
|
import jadx.core.dex.info.AccessInfo.AFType;
|
||||||
@@ -22,9 +21,9 @@ public class FieldNode extends LineAttrNode implements ICodeNode {
|
|||||||
private List<MethodNode> useIn = Collections.emptyList();
|
private List<MethodNode> useIn = Collections.emptyList();
|
||||||
|
|
||||||
public static FieldNode build(ClassNode cls, IFieldData fieldData) {
|
public static FieldNode build(ClassNode cls, IFieldData fieldData) {
|
||||||
FieldInfo fieldInfo = FieldInfo.fromData(cls.root(), fieldData);
|
FieldInfo fieldInfo = FieldInfo.fromRef(cls.root(), fieldData);
|
||||||
FieldNode fieldNode = new FieldNode(cls, fieldInfo, fieldData.getAccessFlags());
|
FieldNode fieldNode = new FieldNode(cls, fieldInfo, fieldData.getAccessFlags());
|
||||||
AnnotationsList.attach(fieldNode, fieldData.getAnnotations());
|
fieldNode.addAttrs(fieldData.getAttributes());
|
||||||
return fieldNode;
|
return fieldNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,13 +2,13 @@ package jadx.core.dex.nodes;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import jadx.api.plugins.input.data.attributes.IJadxAttribute;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.IAttribute;
|
|
||||||
import jadx.core.dex.info.MethodInfo;
|
import jadx.core.dex.info.MethodInfo;
|
||||||
import jadx.core.dex.instructions.args.ArgType;
|
import jadx.core.dex.instructions.args.ArgType;
|
||||||
import jadx.core.utils.Utils;
|
import jadx.core.utils.Utils;
|
||||||
|
|
||||||
public interface IMethodDetails extends IAttribute {
|
public interface IMethodDetails extends IJadxAttribute {
|
||||||
|
|
||||||
MethodInfo getMethodInfo();
|
MethodInfo getMethodInfo();
|
||||||
|
|
||||||
@@ -25,7 +25,7 @@ public interface IMethodDetails extends IAttribute {
|
|||||||
int getRawAccessFlags();
|
int getRawAccessFlags();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
default AType<IMethodDetails> getType() {
|
default AType<IMethodDetails> getAttrType() {
|
||||||
return AType.METHOD_DETAILS;
|
return AType.METHOD_DETAILS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,12 +5,14 @@ import java.util.Collection;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
import jadx.api.ICodeWriter;
|
import jadx.api.ICodeWriter;
|
||||||
import jadx.api.plugins.input.insns.InsnData;
|
import jadx.api.plugins.input.insns.InsnData;
|
||||||
import jadx.core.dex.attributes.AFlag;
|
import jadx.core.dex.attributes.AFlag;
|
||||||
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.nodes.LineAttrNode;
|
import jadx.core.dex.attributes.nodes.LineAttrNode;
|
||||||
import jadx.core.dex.instructions.InsnType;
|
import jadx.core.dex.instructions.InsnType;
|
||||||
import jadx.core.dex.instructions.args.ArgType;
|
import jadx.core.dex.instructions.args.ArgType;
|
||||||
@@ -290,6 +292,18 @@ public class InsnNode extends LineAttrNode {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Visit this instruction and all inner (wrapped) instructions
|
||||||
|
*/
|
||||||
|
public void visitInsns(Consumer<InsnNode> visitor) {
|
||||||
|
visitor.accept(this);
|
||||||
|
for (InsnArg arg : this.getArguments()) {
|
||||||
|
if (arg.isInsnWrap()) {
|
||||||
|
((InsnWrapArg) arg).getWrapInsn().visitInsns(visitor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 'Soft' equals, don't compare arguments, only instruction specific parameters.
|
* 'Soft' equals, don't compare arguments, only instruction specific parameters.
|
||||||
*/
|
*/
|
||||||
@@ -346,6 +360,11 @@ public class InsnNode extends LineAttrNode {
|
|||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void copyAttributesFrom(InsnNode attrNode) {
|
||||||
|
super.copyAttributesFrom(attrNode);
|
||||||
|
this.addSourceLineFrom(attrNode);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Make copy of InsnNode object.
|
* Make copy of InsnNode object.
|
||||||
* <p>
|
* <p>
|
||||||
@@ -451,6 +470,21 @@ public class InsnNode extends LineAttrNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void inheritMetadata(InsnNode sourceInsn) {
|
||||||
|
if (insnType == InsnType.RETURN) {
|
||||||
|
this.copyLines(sourceInsn);
|
||||||
|
if (this.contains(AFlag.SYNTHETIC)) {
|
||||||
|
this.setOffset(sourceInsn.getOffset());
|
||||||
|
this.rewriteAttributeFrom(sourceInsn, AType.CODE_COMMENTS);
|
||||||
|
} else {
|
||||||
|
this.copyAttributeFrom(sourceInsn, AType.CODE_COMMENTS);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.copyAttributeFrom(sourceInsn, AType.CODE_COMMENTS);
|
||||||
|
this.addSourceLineFrom(sourceInsn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compare instruction only by identity.
|
* Compare instruction only by identity.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -13,13 +13,10 @@ import org.slf4j.LoggerFactory;
|
|||||||
import jadx.api.plugins.input.data.ICodeReader;
|
import jadx.api.plugins.input.data.ICodeReader;
|
||||||
import jadx.api.plugins.input.data.IDebugInfo;
|
import jadx.api.plugins.input.data.IDebugInfo;
|
||||||
import jadx.api.plugins.input.data.IMethodData;
|
import jadx.api.plugins.input.data.IMethodData;
|
||||||
import jadx.api.plugins.input.data.annotations.EncodedValue;
|
import jadx.api.plugins.input.data.attributes.JadxAttrType;
|
||||||
import jadx.api.plugins.input.data.annotations.IAnnotation;
|
import jadx.api.plugins.input.data.attributes.types.ExceptionsAttr;
|
||||||
import jadx.core.Consts;
|
|
||||||
import jadx.core.codegen.NameGen;
|
import jadx.core.codegen.NameGen;
|
||||||
import jadx.core.dex.attributes.AFlag;
|
import jadx.core.dex.attributes.AFlag;
|
||||||
import jadx.core.dex.attributes.annotations.AnnotationsList;
|
|
||||||
import jadx.core.dex.attributes.annotations.MethodParameters;
|
|
||||||
import jadx.core.dex.attributes.nodes.LoopInfo;
|
import jadx.core.dex.attributes.nodes.LoopInfo;
|
||||||
import jadx.core.dex.attributes.nodes.NotificationAttrNode;
|
import jadx.core.dex.attributes.nodes.NotificationAttrNode;
|
||||||
import jadx.core.dex.info.AccessInfo;
|
import jadx.core.dex.info.AccessInfo;
|
||||||
@@ -52,11 +49,11 @@ public class MethodNode extends NotificationAttrNode implements IMethodDetails,
|
|||||||
private AccessInfo accFlags;
|
private AccessInfo accFlags;
|
||||||
|
|
||||||
private final ICodeReader codeReader;
|
private final ICodeReader codeReader;
|
||||||
private final boolean methodIsVirtual;
|
|
||||||
private final int insnsCount;
|
private final int insnsCount;
|
||||||
|
|
||||||
private boolean noCode;
|
private boolean noCode;
|
||||||
private int regsCount;
|
private int regsCount;
|
||||||
|
private int argsStartReg;
|
||||||
|
|
||||||
private boolean loaded;
|
private boolean loaded;
|
||||||
|
|
||||||
@@ -82,8 +79,7 @@ public class MethodNode extends NotificationAttrNode implements IMethodDetails,
|
|||||||
|
|
||||||
public static MethodNode build(ClassNode classNode, IMethodData methodData) {
|
public static MethodNode build(ClassNode classNode, IMethodData methodData) {
|
||||||
MethodNode methodNode = new MethodNode(classNode, methodData);
|
MethodNode methodNode = new MethodNode(classNode, methodData);
|
||||||
AnnotationsList.attach(methodNode, methodData.getAnnotations());
|
methodNode.addAttrs(methodData.getAttributes());
|
||||||
MethodParameters.attach(methodNode, methodData.getParamsAnnotations());
|
|
||||||
return methodNode;
|
return methodNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,7 +87,6 @@ public class MethodNode extends NotificationAttrNode implements IMethodDetails,
|
|||||||
this.mthInfo = MethodInfo.fromRef(classNode.root(), mthData.getMethodRef());
|
this.mthInfo = MethodInfo.fromRef(classNode.root(), mthData.getMethodRef());
|
||||||
this.parentClass = classNode;
|
this.parentClass = classNode;
|
||||||
this.accFlags = new AccessInfo(mthData.getAccessFlags(), AFType.METHOD);
|
this.accFlags = new AccessInfo(mthData.getAccessFlags(), AFType.METHOD);
|
||||||
this.methodIsVirtual = !mthData.isDirect();
|
|
||||||
ICodeReader codeReader = mthData.getCodeReader();
|
ICodeReader codeReader = mthData.getCodeReader();
|
||||||
this.noCode = codeReader == null;
|
this.noCode = codeReader == null;
|
||||||
if (noCode) {
|
if (noCode) {
|
||||||
@@ -99,7 +94,7 @@ public class MethodNode extends NotificationAttrNode implements IMethodDetails,
|
|||||||
this.insnsCount = 0;
|
this.insnsCount = 0;
|
||||||
} else {
|
} else {
|
||||||
this.codeReader = codeReader.copy();
|
this.codeReader = codeReader.copy();
|
||||||
this.insnsCount = codeReader.getInsnsCount();
|
this.insnsCount = codeReader.getUnitsCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.retType = mthInfo.getReturnType();
|
this.retType = mthInfo.getReturnType();
|
||||||
@@ -194,6 +189,7 @@ public class MethodNode extends NotificationAttrNode implements IMethodDetails,
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.regsCount = codeReader.getRegistersCount();
|
this.regsCount = codeReader.getRegistersCount();
|
||||||
|
this.argsStartReg = codeReader.getArgsStartReg();
|
||||||
initArguments(this.argTypes);
|
initArguments(this.argTypes);
|
||||||
InsnDecoder decoder = new InsnDecoder(this);
|
InsnDecoder decoder = new InsnDecoder(this);
|
||||||
this.instructions = decoder.process(codeReader);
|
this.instructions = decoder.process(codeReader);
|
||||||
@@ -205,7 +201,8 @@ public class MethodNode extends NotificationAttrNode implements IMethodDetails,
|
|||||||
load();
|
load();
|
||||||
noCode = false;
|
noCode = false;
|
||||||
}
|
}
|
||||||
throw new DecodeException(this, "Load method exception: " + e.getMessage(), e);
|
throw new DecodeException(this, "Load method exception: "
|
||||||
|
+ e.getClass().getSimpleName() + ": " + e.getMessage(), e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,21 +237,13 @@ public class MethodNode extends NotificationAttrNode implements IMethodDetails,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initArguments(List<ArgType> args) {
|
private void initArguments(List<ArgType> args) {
|
||||||
int pos;
|
int pos = getArgsStartPos(args);
|
||||||
if (noCode) {
|
|
||||||
pos = 1;
|
|
||||||
} else {
|
|
||||||
pos = regsCount;
|
|
||||||
for (ArgType arg : args) {
|
|
||||||
pos -= arg.getRegCount();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TypeUtils typeUtils = root().getTypeUtils();
|
TypeUtils typeUtils = root().getTypeUtils();
|
||||||
if (accFlags.isStatic()) {
|
if (accFlags.isStatic()) {
|
||||||
thisArg = null;
|
thisArg = null;
|
||||||
} else {
|
} else {
|
||||||
ArgType thisClsType = typeUtils.expandTypeVariables(this, parentClass.getType());
|
ArgType thisClsType = typeUtils.expandTypeVariables(this, parentClass.getType());
|
||||||
RegisterArg arg = InsnArg.reg(pos - 1, thisClsType);
|
RegisterArg arg = InsnArg.reg(pos++, thisClsType);
|
||||||
arg.add(AFlag.THIS);
|
arg.add(AFlag.THIS);
|
||||||
arg.add(AFlag.IMMUTABLE_TYPE);
|
arg.add(AFlag.IMMUTABLE_TYPE);
|
||||||
thisArg = arg;
|
thisArg = arg;
|
||||||
@@ -274,6 +263,23 @@ public class MethodNode extends NotificationAttrNode implements IMethodDetails,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int getArgsStartPos(List<ArgType> args) {
|
||||||
|
if (noCode) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
if (argsStartReg != -1) {
|
||||||
|
return argsStartReg;
|
||||||
|
}
|
||||||
|
int pos = regsCount;
|
||||||
|
for (ArgType arg : args) {
|
||||||
|
pos -= arg.getRegCount();
|
||||||
|
}
|
||||||
|
if (!accFlags.isStatic()) {
|
||||||
|
pos--;
|
||||||
|
}
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@NotNull
|
@NotNull
|
||||||
public List<ArgType> getArgTypes() {
|
public List<ArgType> getArgTypes() {
|
||||||
@@ -480,14 +486,12 @@ public class MethodNode extends NotificationAttrNode implements IMethodDetails,
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public List<ArgType> getThrows() {
|
public List<ArgType> getThrows() {
|
||||||
IAnnotation an = getAnnotation(Consts.DALVIK_THROWS);
|
ExceptionsAttr exceptionsAttr = get(JadxAttrType.EXCEPTIONS);
|
||||||
if (an == null) {
|
if (exceptionsAttr == null) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
List<EncodedValue> types = (List<EncodedValue>) an.getDefaultValue().getValue();
|
return Utils.collectionMap(exceptionsAttr.getList(), ArgType::object);
|
||||||
return Utils.collectionMap(types, ev -> ArgType.object((String) ev.getValue()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -528,10 +532,6 @@ public class MethodNode extends NotificationAttrNode implements IMethodDetails,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isVirtual() {
|
|
||||||
return methodIsVirtual;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getRegsCount() {
|
public int getRegsCount() {
|
||||||
return regsCount;
|
return regsCount;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -243,8 +243,9 @@ public class RootNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void runPreDecompileStage() {
|
public void runPreDecompileStage() {
|
||||||
|
boolean debugEnabled = LOG.isDebugEnabled();
|
||||||
for (IDexTreeVisitor pass : preDecompilePasses) {
|
for (IDexTreeVisitor pass : preDecompilePasses) {
|
||||||
long start = System.currentTimeMillis();
|
long start = debugEnabled ? System.currentTimeMillis() : 0;
|
||||||
try {
|
try {
|
||||||
pass.init(this);
|
pass.init(this);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -253,7 +254,7 @@ public class RootNode {
|
|||||||
for (ClassNode cls : classes) {
|
for (ClassNode cls : classes) {
|
||||||
DepthTraversal.visit(pass, cls);
|
DepthTraversal.visit(pass, cls);
|
||||||
}
|
}
|
||||||
if (LOG.isDebugEnabled()) {
|
if (debugEnabled) {
|
||||||
LOG.debug("{} time: {}ms", pass.getClass().getSimpleName(), System.currentTimeMillis() - start);
|
LOG.debug("{} time: {}ms", pass.getClass().getSimpleName(), System.currentTimeMillis() - start);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,12 +9,10 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import jadx.api.plugins.input.data.annotations.EncodedValue;
|
import jadx.api.plugins.input.data.attributes.JadxAttrType;
|
||||||
import jadx.api.plugins.input.data.annotations.IAnnotation;
|
import jadx.api.plugins.input.data.attributes.types.SignatureAttr;
|
||||||
import jadx.core.Consts;
|
|
||||||
import jadx.core.dex.attributes.IAttributeNode;
|
import jadx.core.dex.attributes.IAttributeNode;
|
||||||
import jadx.core.dex.instructions.args.ArgType;
|
import jadx.core.dex.instructions.args.ArgType;
|
||||||
import jadx.core.utils.Utils;
|
|
||||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||||
|
|
||||||
public class SignatureParser {
|
public class SignatureParser {
|
||||||
@@ -43,16 +41,13 @@ public class SignatureParser {
|
|||||||
return new SignatureParser(signature);
|
return new SignatureParser(signature);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public static String getSignature(IAttributeNode node) {
|
public static String getSignature(IAttributeNode node) {
|
||||||
IAnnotation a = node.getAnnotation(Consts.DALVIK_SIGNATURE);
|
SignatureAttr attr = node.get(JadxAttrType.SIGNATURE);
|
||||||
if (a == null) {
|
if (attr == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
List<EncodedValue> values = (List<EncodedValue>) a.getDefaultValue().getValue();
|
return attr.getSignature();
|
||||||
List<String> strings = Utils.collectionMap(values, ev -> ((String) ev.getValue()));
|
|
||||||
return mergeSignature(strings);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private char next() {
|
private char next() {
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
@@ -253,7 +253,7 @@ public final class IfCondition extends AttrNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<RegisterArg> getRegisterArgs() {
|
public List<RegisterArg> getRegisterArgs() {
|
||||||
List<RegisterArg> list = new LinkedList<>();
|
List<RegisterArg> list = new ArrayList<>();
|
||||||
if (mode == Mode.COMPARE) {
|
if (mode == Mode.COMPARE) {
|
||||||
compare.getInsn().getRegisterArgs(list);
|
compare.getInsn().getRegisterArgs(list);
|
||||||
} else {
|
} else {
|
||||||
@@ -264,6 +264,14 @@ public final class IfCondition extends AttrNode {
|
|||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void visitInsns(Consumer<InsnNode> visitor) {
|
||||||
|
if (mode == Mode.COMPARE) {
|
||||||
|
compare.getInsn().visitInsns(visitor);
|
||||||
|
} else {
|
||||||
|
args.forEach(arg -> arg.visitInsns(visitor));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public InsnNode getFirstInsn() {
|
public InsnNode getFirstInsn() {
|
||||||
if (mode == Mode.COMPARE) {
|
if (mode == Mode.COMPARE) {
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package jadx.core.dex.trycatch;
|
package jadx.core.dex.trycatch;
|
||||||
|
|
||||||
|
import jadx.api.plugins.input.data.attributes.IJadxAttribute;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.IAttribute;
|
|
||||||
|
|
||||||
public class CatchAttr implements IAttribute {
|
public class CatchAttr implements IJadxAttribute {
|
||||||
|
|
||||||
private final TryCatchBlock tryBlock;
|
private final TryCatchBlock tryBlock;
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ public class CatchAttr implements IAttribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AType<CatchAttr> getType() {
|
public AType<CatchAttr> getAttrType() {
|
||||||
return AType.CATCH_BLOCK;
|
return AType.CATCH_BLOCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package jadx.core.dex.trycatch;
|
package jadx.core.dex.trycatch;
|
||||||
|
|
||||||
|
import jadx.api.plugins.input.data.attributes.IJadxAttribute;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.IAttribute;
|
|
||||||
|
|
||||||
public class ExcHandlerAttr implements IAttribute {
|
public class ExcHandlerAttr implements IJadxAttribute {
|
||||||
|
|
||||||
private final TryCatchBlock tryBlock;
|
private final TryCatchBlock tryBlock;
|
||||||
private final ExceptionHandler handler;
|
private final ExceptionHandler handler;
|
||||||
@@ -14,7 +14,7 @@ public class ExcHandlerAttr implements IAttribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AType<ExcHandlerAttr> getType() {
|
public AType<ExcHandlerAttr> getAttrType() {
|
||||||
return AType.EXC_HANDLER;
|
return AType.EXC_HANDLER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
package jadx.core.dex.trycatch;
|
package jadx.core.dex.trycatch;
|
||||||
|
|
||||||
|
import jadx.api.plugins.input.data.attributes.IJadxAttribute;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.IAttribute;
|
|
||||||
import jadx.core.dex.nodes.BlockNode;
|
import jadx.core.dex.nodes.BlockNode;
|
||||||
|
|
||||||
public class SplitterBlockAttr implements IAttribute {
|
public class SplitterBlockAttr implements IJadxAttribute {
|
||||||
|
|
||||||
private final BlockNode block;
|
private final BlockNode block;
|
||||||
|
|
||||||
@@ -17,7 +17,7 @@ public class SplitterBlockAttr implements IAttribute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AType<SplitterBlockAttr> getType() {
|
public AType<SplitterBlockAttr> getAttrType() {
|
||||||
return AType.SPLITTER_BLOCK;
|
return AType.SPLITTER_BLOCK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -86,12 +86,11 @@ public class AttachTryCatchVisitor extends AbstractVisitor {
|
|||||||
markTryBounds(insnByOffset, tryData, catchBlock);
|
markTryBounds(insnByOffset, tryData, catchBlock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void markTryBounds(InsnNode[] insnByOffset, ITry aTry, TryCatchBlock catchBlock) {
|
private static void markTryBounds(InsnNode[] insnByOffset, ITry aTry, TryCatchBlock catchBlock) {
|
||||||
int offset = aTry.getStartAddress();
|
int offset = aTry.getStartAddress();
|
||||||
int end = offset + aTry.getInstructionCount() - 1;
|
int end = aTry.getEndAddress();
|
||||||
|
|
||||||
boolean tryBlockStarted = false;
|
boolean tryBlockStarted = false;
|
||||||
InsnNode insn = null;
|
InsnNode insn = null;
|
||||||
|
|||||||
@@ -303,15 +303,17 @@ public class ClassModifier extends AbstractVisitor {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove public empty constructors (static or default)
|
||||||
|
*/
|
||||||
private static void removeEmptyMethods(MethodNode mth) {
|
private static void removeEmptyMethods(MethodNode mth) {
|
||||||
AccessInfo af = mth.getAccessFlags();
|
AccessInfo af = mth.getAccessFlags();
|
||||||
// remove public empty constructors (static or default)
|
boolean publicConstructor = af.isConstructor() && af.isPublic();
|
||||||
if (af.isConstructor()
|
boolean clsInit = mth.getMethodInfo().isClassInit() && af.isStatic();
|
||||||
&& (af.isPublic() || af.isStatic())
|
if ((publicConstructor || clsInit) && mth.getArgRegs().isEmpty()) {
|
||||||
&& mth.getArgRegs().isEmpty()) {
|
|
||||||
List<BlockNode> bb = mth.getBasicBlocks();
|
List<BlockNode> bb = mth.getBasicBlocks();
|
||||||
if (bb == null || bb.isEmpty() || BlockUtils.isAllBlocksEmpty(bb)) {
|
if (bb == null || bb.isEmpty() || BlockUtils.isAllBlocksEmpty(bb)) {
|
||||||
if (af.isStatic() && mth.getMethodInfo().isClassInit()) {
|
if (clsInit) {
|
||||||
mth.add(AFlag.DONT_GENERATE);
|
mth.add(AFlag.DONT_GENERATE);
|
||||||
} else {
|
} else {
|
||||||
// don't remove default constructor if other constructors exists
|
// don't remove default constructor if other constructors exists
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import jadx.core.dex.attributes.AFlag;
|
import jadx.core.dex.attributes.AFlag;
|
||||||
import jadx.core.dex.attributes.AType;
|
|
||||||
import jadx.core.dex.info.MethodInfo;
|
import jadx.core.dex.info.MethodInfo;
|
||||||
import jadx.core.dex.instructions.BaseInvokeNode;
|
import jadx.core.dex.instructions.BaseInvokeNode;
|
||||||
import jadx.core.dex.instructions.ConstStringNode;
|
import jadx.core.dex.instructions.ConstStringNode;
|
||||||
@@ -157,7 +156,7 @@ public class ConstInlineVisitor extends AbstractVisitor {
|
|||||||
List<RegisterArg> useList = new ArrayList<>(ssaVar.getUseList());
|
List<RegisterArg> useList = new ArrayList<>(ssaVar.getUseList());
|
||||||
int replaceCount = 0;
|
int replaceCount = 0;
|
||||||
for (RegisterArg arg : useList) {
|
for (RegisterArg arg : useList) {
|
||||||
if (canInline(arg) && replaceArg(mth, arg, constArg, constInsn, toRemove)) {
|
if (canInline(arg) && replaceArg(mth, arg, constArg, constInsn)) {
|
||||||
replaceCount++;
|
replaceCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -180,7 +179,7 @@ public class ConstInlineVisitor extends AbstractVisitor {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean replaceArg(MethodNode mth, RegisterArg arg, InsnArg constArg, InsnNode constInsn, List<InsnNode> toRemove) {
|
private static boolean replaceArg(MethodNode mth, RegisterArg arg, InsnArg constArg, InsnNode constInsn) {
|
||||||
InsnNode useInsn = arg.getParentInsn();
|
InsnNode useInsn = arg.getParentInsn();
|
||||||
if (useInsn == null) {
|
if (useInsn == null) {
|
||||||
return false;
|
return false;
|
||||||
@@ -224,15 +223,7 @@ public class ConstInlineVisitor extends AbstractVisitor {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (insnType == InsnType.RETURN) {
|
useInsn.inheritMetadata(constInsn);
|
||||||
useInsn.setSourceLine(constInsn.getSourceLine());
|
|
||||||
if (useInsn.contains(AFlag.SYNTHETIC)) {
|
|
||||||
useInsn.setOffset(constInsn.getOffset());
|
|
||||||
useInsn.rewriteAttributeFrom(constInsn, AType.CODE_COMMENTS);
|
|
||||||
} else {
|
|
||||||
useInsn.copyAttributeFrom(constInsn, AType.CODE_COMMENTS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,6 +64,8 @@ public class ConstructorVisitor extends AbstractVisitor {
|
|||||||
remover.addAndUnbind(inv);
|
remover.addAndUnbind(inv);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
co.inheritMetadata(inv);
|
||||||
|
|
||||||
RegisterArg instanceArg = ((RegisterArg) inv.getArg(0));
|
RegisterArg instanceArg = ((RegisterArg) inv.getArg(0));
|
||||||
InsnNode newInstInsn = null;
|
InsnNode newInstInsn = null;
|
||||||
if (co.isNewInstance()) {
|
if (co.isNewInstance()) {
|
||||||
@@ -97,6 +99,7 @@ public class ConstructorVisitor extends AbstractVisitor {
|
|||||||
parentInsn.replaceArg(useArg, resultArg.duplicate());
|
parentInsn.replaceArg(useArg, resultArg.duplicate());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
co.inheritMetadata(newInstInsn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ConstructorInsn replace = processConstructor(mth, co);
|
ConstructorInsn replace = processConstructor(mth, co);
|
||||||
@@ -154,6 +157,7 @@ public class ConstructorVisitor extends AbstractVisitor {
|
|||||||
}
|
}
|
||||||
ConstructorInsn newInsn = new ConstructorInsn(defCtr.getMethodInfo(), co.getCallType());
|
ConstructorInsn newInsn = new ConstructorInsn(defCtr.getMethodInfo(), co.getCallType());
|
||||||
newInsn.setResult(co.getResult().duplicate());
|
newInsn.setResult(co.getResult().duplicate());
|
||||||
|
newInsn.inheritMetadata(co);
|
||||||
return newInsn;
|
return newInsn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package jadx.core.dex.visitors;
|
package jadx.core.dex.visitors;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
@@ -86,13 +87,30 @@ public class DotGraphVisitor extends AbstractVisitor {
|
|||||||
dot.add(escape(mth.getParentClass() + "." + mth.getMethodInfo().getShortId()));
|
dot.add(escape(mth.getParentClass() + "." + mth.getMethodInfo().getShortId()));
|
||||||
dot.add("\" {");
|
dot.add("\" {");
|
||||||
|
|
||||||
|
BlockNode enterBlock = mth.getEnterBlock();
|
||||||
if (useRegions) {
|
if (useRegions) {
|
||||||
if (mth.getRegion() == null) {
|
if (mth.getRegion() == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
processMethodRegion(mth);
|
processMethodRegion(mth);
|
||||||
} else {
|
} else {
|
||||||
for (BlockNode block : mth.getBasicBlocks()) {
|
List<BlockNode> blocks = mth.getBasicBlocks();
|
||||||
|
if (blocks == null) {
|
||||||
|
InsnNode[] insnArr = mth.getInstructions();
|
||||||
|
if (insnArr == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
BlockNode block = new BlockNode(0, 0);
|
||||||
|
List<InsnNode> insnList = block.getInstructions();
|
||||||
|
for (InsnNode insn : insnArr) {
|
||||||
|
if (insn != null) {
|
||||||
|
insnList.add(insn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enterBlock = block;
|
||||||
|
blocks = Collections.singletonList(block);
|
||||||
|
}
|
||||||
|
for (BlockNode block : blocks) {
|
||||||
processBlock(mth, block, false);
|
processBlock(mth, block, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,7 +127,7 @@ public class DotGraphVisitor extends AbstractVisitor {
|
|||||||
}
|
}
|
||||||
dot.add("}\"];");
|
dot.add("}\"];");
|
||||||
|
|
||||||
dot.startLine("MethodNode -> ").add(makeName(mth.getEnterBlock())).add(';');
|
dot.startLine("MethodNode -> ").add(makeName(enterBlock)).add(';');
|
||||||
|
|
||||||
dot.add(conn.toString());
|
dot.add(conn.toString());
|
||||||
|
|
||||||
@@ -269,6 +287,9 @@ public class DotGraphVisitor extends AbstractVisitor {
|
|||||||
StringBuilder str = new StringBuilder();
|
StringBuilder str = new StringBuilder();
|
||||||
for (InsnNode insn : block.getInstructions()) {
|
for (InsnNode insn : block.getInstructions()) {
|
||||||
str.append(escape(insn + " " + insn.getAttributesString()));
|
str.append(escape(insn + " " + insn.getAttributesString()));
|
||||||
|
if (insn.getSourceLine() != 0) {
|
||||||
|
str.append(" (LINE:").append(insn.getSourceLine()).append(')');
|
||||||
|
}
|
||||||
str.append(NL);
|
str.append(NL);
|
||||||
}
|
}
|
||||||
return str.toString();
|
return str.toString();
|
||||||
|
|||||||
@@ -6,9 +6,10 @@ import java.util.HashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import jadx.api.plugins.input.data.attributes.JadxAttrType;
|
||||||
import jadx.core.dex.attributes.AFlag;
|
import jadx.core.dex.attributes.AFlag;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.fldinit.FieldInitAttr;
|
import jadx.core.dex.attributes.FieldInitInsnAttr;
|
||||||
import jadx.core.dex.info.AccessInfo;
|
import jadx.core.dex.info.AccessInfo;
|
||||||
import jadx.core.dex.info.FieldInfo;
|
import jadx.core.dex.info.FieldInfo;
|
||||||
import jadx.core.dex.instructions.IndexInsnNode;
|
import jadx.core.dex.instructions.IndexInsnNode;
|
||||||
@@ -71,7 +72,7 @@ public class ExtractFieldInit extends AbstractVisitor {
|
|||||||
if (field.getDeclClass().equals(cls.getClassInfo())) {
|
if (field.getDeclClass().equals(cls.getClassInfo())) {
|
||||||
FieldNode fn = cls.searchField(field);
|
FieldNode fn = cls.searchField(field);
|
||||||
if (fn != null && fn.getAccessFlags().isFinal()) {
|
if (fn != null && fn.getAccessFlags().isFinal()) {
|
||||||
fn.remove(AType.FIELD_INIT);
|
fn.remove(JadxAttrType.CONSTANT_VALUE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -90,7 +91,7 @@ public class ExtractFieldInit extends AbstractVisitor {
|
|||||||
private static boolean processFields(ClassNode cls, MethodNode classInitMth) {
|
private static boolean processFields(ClassNode cls, MethodNode classInitMth) {
|
||||||
boolean changed = false;
|
boolean changed = false;
|
||||||
for (FieldNode field : cls.getFields()) {
|
for (FieldNode field : cls.getFields()) {
|
||||||
if (field.contains(AFlag.DONT_GENERATE) || field.contains(AType.FIELD_INIT)) {
|
if (field.contains(AFlag.DONT_GENERATE) || field.contains(AType.FIELD_INIT_INSN)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (field.getAccessFlags().isStatic()) {
|
if (field.getAccessFlags().isStatic()) {
|
||||||
@@ -277,6 +278,6 @@ public class ExtractFieldInit extends AbstractVisitor {
|
|||||||
|
|
||||||
private static void addFieldInitAttr(MethodNode classInitMth, FieldNode field, InsnNode insn) {
|
private static void addFieldInitAttr(MethodNode classInitMth, FieldNode field, InsnNode insn) {
|
||||||
InsnNode assignInsn = InsnNode.wrapArg(insn.getArg(0));
|
InsnNode assignInsn = InsnNode.wrapArg(insn.getArg(0));
|
||||||
field.addAttr(FieldInitAttr.insnValue(classInitMth, assignInsn));
|
field.addAttr(new FieldInitInsnAttr(classInitMth, assignInsn));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
package jadx.core.dex.visitors;
|
package jadx.core.dex.visitors;
|
||||||
|
|
||||||
import jadx.api.plugins.input.data.AccessFlags;
|
import jadx.api.plugins.input.data.AccessFlags;
|
||||||
|
import jadx.core.dex.attributes.AFlag;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
|
import jadx.core.dex.attributes.nodes.MethodOverrideAttr;
|
||||||
import jadx.core.dex.info.AccessInfo;
|
import jadx.core.dex.info.AccessInfo;
|
||||||
import jadx.core.dex.nodes.ClassNode;
|
import jadx.core.dex.nodes.ClassNode;
|
||||||
import jadx.core.dex.nodes.ICodeNode;
|
import jadx.core.dex.nodes.ICodeNode;
|
||||||
|
import jadx.core.dex.nodes.IMethodDetails;
|
||||||
import jadx.core.dex.nodes.MethodNode;
|
import jadx.core.dex.nodes.MethodNode;
|
||||||
import jadx.core.dex.nodes.RootNode;
|
import jadx.core.dex.nodes.RootNode;
|
||||||
import jadx.core.utils.exceptions.JadxException;
|
import jadx.core.utils.exceptions.JadxException;
|
||||||
@@ -37,7 +40,7 @@ public class FixAccessModifiers extends AbstractVisitor {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(MethodNode mth) {
|
public void visit(MethodNode mth) {
|
||||||
if (respectAccessModifiers) {
|
if (respectAccessModifiers || mth.contains(AFlag.DONT_GENERATE)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int newVisFlag = fixMethodVisibility(mth);
|
int newVisFlag = fixMethodVisibility(mth);
|
||||||
@@ -93,27 +96,30 @@ public class FixAccessModifiers extends AbstractVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static int fixMethodVisibility(MethodNode mth) {
|
private static int fixMethodVisibility(MethodNode mth) {
|
||||||
if (mth.isVirtual()) {
|
AccessInfo accessFlags = mth.getAccessFlags();
|
||||||
// make virtual methods public
|
if (accessFlags.isPublic()) {
|
||||||
return AccessFlags.PUBLIC;
|
return -1;
|
||||||
} else {
|
}
|
||||||
AccessInfo accessFlags = mth.getAccessFlags();
|
MethodOverrideAttr overrideAttr = mth.get(AType.METHOD_OVERRIDE);
|
||||||
if (accessFlags.isAbstract()) {
|
if (overrideAttr != null && !overrideAttr.getOverrideList().isEmpty()) {
|
||||||
// make abstract methods public
|
// visibility can't be weaker
|
||||||
|
IMethodDetails parentMD = overrideAttr.getOverrideList().get(0);
|
||||||
|
AccessInfo parentAccInfo = new AccessInfo(parentMD.getRawAccessFlags(), AccessInfo.AFType.METHOD);
|
||||||
|
if (accessFlags.isVisibilityWeakerThan(parentAccInfo)) {
|
||||||
|
return parentAccInfo.getVisibility().rawValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mth.getUseIn().isEmpty()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassNode thisTopParentCls = mth.getParentClass().getTopParentClass();
|
||||||
|
for (MethodNode useMth : mth.getUseIn()) {
|
||||||
|
ClassNode useInTPCls = useMth.getParentClass().getTopParentClass();
|
||||||
|
if (!useInTPCls.equals(thisTopParentCls)) {
|
||||||
return AccessFlags.PUBLIC;
|
return AccessFlags.PUBLIC;
|
||||||
}
|
}
|
||||||
// enum constructor can't be public
|
|
||||||
if (accessFlags.isConstructor()
|
|
||||||
&& accessFlags.isPublic()
|
|
||||||
&& mth.getParentClass().isEnum()) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (accessFlags.isConstructor() || accessFlags.isStatic()) {
|
|
||||||
// TODO: make public if used outside
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
// make other direct methods private
|
|
||||||
return AccessFlags.PRIVATE;
|
|
||||||
}
|
}
|
||||||
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,19 +63,11 @@ public class MethodInvokeVisitor extends AbstractVisitor {
|
|||||||
if (insn.contains(AFlag.DONT_GENERATE)) {
|
if (insn.contains(AFlag.DONT_GENERATE)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
processInsn(mth, insn);
|
insn.visitInsns(in -> {
|
||||||
}
|
if (in instanceof BaseInvokeNode) {
|
||||||
}
|
processInvoke(mth, ((BaseInvokeNode) in));
|
||||||
}
|
}
|
||||||
|
});
|
||||||
private void processInsn(MethodNode mth, InsnNode insn) {
|
|
||||||
if (insn instanceof BaseInvokeNode) {
|
|
||||||
processInvoke(mth, ((BaseInvokeNode) insn));
|
|
||||||
}
|
|
||||||
for (InsnArg insnArg : insn.getArguments()) {
|
|
||||||
if (insnArg instanceof InsnWrapArg) {
|
|
||||||
InsnNode wrapInsn = ((InsnWrapArg) insnArg).getWrapInsn();
|
|
||||||
processInsn(mth, wrapInsn);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -92,7 +84,6 @@ public class MethodInvokeVisitor extends AbstractVisitor {
|
|||||||
}
|
}
|
||||||
processUnknown(invokeInsn);
|
processUnknown(invokeInsn);
|
||||||
} else {
|
} else {
|
||||||
// parentMth.addComment("JADX DEBUG: got method details: " + mthDetails);
|
|
||||||
if (mthDetails.isVarArg()) {
|
if (mthDetails.isVarArg()) {
|
||||||
ArgType last = Utils.last(mthDetails.getArgTypes());
|
ArgType last = Utils.last(mthDetails.getArgTypes());
|
||||||
if (last != null && last.isArray()) {
|
if (last != null && last.isArray()) {
|
||||||
|
|||||||
@@ -14,10 +14,11 @@ import jadx.api.plugins.input.data.annotations.AnnotationVisibility;
|
|||||||
import jadx.api.plugins.input.data.annotations.EncodedType;
|
import jadx.api.plugins.input.data.annotations.EncodedType;
|
||||||
import jadx.api.plugins.input.data.annotations.EncodedValue;
|
import jadx.api.plugins.input.data.annotations.EncodedValue;
|
||||||
import jadx.api.plugins.input.data.annotations.IAnnotation;
|
import jadx.api.plugins.input.data.annotations.IAnnotation;
|
||||||
|
import jadx.api.plugins.input.data.attributes.JadxAttrType;
|
||||||
|
import jadx.api.plugins.input.data.attributes.types.AnnotationsAttr;
|
||||||
import jadx.core.dex.attributes.AFlag;
|
import jadx.core.dex.attributes.AFlag;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.AttrNode;
|
import jadx.core.dex.attributes.AttrNode;
|
||||||
import jadx.core.dex.attributes.annotations.AnnotationsList;
|
|
||||||
import jadx.core.dex.attributes.nodes.FieldReplaceAttr;
|
import jadx.core.dex.attributes.nodes.FieldReplaceAttr;
|
||||||
import jadx.core.dex.info.AccessInfo;
|
import jadx.core.dex.info.AccessInfo;
|
||||||
import jadx.core.dex.info.FieldInfo;
|
import jadx.core.dex.info.FieldInfo;
|
||||||
@@ -276,7 +277,7 @@ public class ModVisitor extends AbstractVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void replaceConstsInAnnotationForAttrNode(ClassNode parentCls, AttrNode attrNode) {
|
private void replaceConstsInAnnotationForAttrNode(ClassNode parentCls, AttrNode attrNode) {
|
||||||
AnnotationsList annotationsList = attrNode.get(AType.ANNOTATION_LIST);
|
AnnotationsAttr annotationsList = attrNode.get(JadxAttrType.ANNOTATION_LIST);
|
||||||
if (annotationsList == null) {
|
if (annotationsList == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ public class MoveInlineVisitor extends AbstractVisitor {
|
|||||||
} else {
|
} else {
|
||||||
replaceArg = moveArg.duplicate();
|
replaceArg = moveArg.duplicate();
|
||||||
}
|
}
|
||||||
|
useInsn.inheritMetadata(move);
|
||||||
replaceArg.copyAttributesFrom(useArg);
|
replaceArg.copyAttributesFrom(useArg);
|
||||||
if (debugInfo != null) {
|
if (debugInfo != null) {
|
||||||
replaceArg.addAttr(debugInfo);
|
replaceArg.addAttr(debugInfo);
|
||||||
|
|||||||
@@ -49,17 +49,12 @@ public class ProcessInstructionsVisitor extends AbstractVisitor {
|
|||||||
switch (insn.getType()) {
|
switch (insn.getType()) {
|
||||||
case SWITCH:
|
case SWITCH:
|
||||||
SwitchInsn sw = (SwitchInsn) insn;
|
SwitchInsn sw = (SwitchInsn) insn;
|
||||||
// default case
|
if (sw.needData()) {
|
||||||
int nextInsnOffset = getNextInsnOffset(insnByOffset, offset);
|
attachSwitchData(insnByOffset, offset, sw);
|
||||||
if (nextInsnOffset != -1) {
|
|
||||||
addJump(mth, insnByOffset, offset, nextInsnOffset);
|
|
||||||
}
|
}
|
||||||
int dataTarget = sw.getDataTarget();
|
int defCaseOffset = sw.getDefaultCaseOffset();
|
||||||
InsnNode switchDataInsn = getInsnAtOffset(insnByOffset, dataTarget);
|
if (defCaseOffset != -1) {
|
||||||
if (switchDataInsn != null && switchDataInsn.getType() == InsnType.SWITCH_DATA) {
|
addJump(mth, insnByOffset, offset, defCaseOffset);
|
||||||
sw.attachSwitchData((SwitchData) switchDataInsn, nextInsnOffset);
|
|
||||||
} else {
|
|
||||||
throw new JadxRuntimeException("Payload for fill-array not found at " + InsnUtils.formatOffset(dataTarget));
|
|
||||||
}
|
}
|
||||||
for (int target : sw.getTargets()) {
|
for (int target : sw.getTargets()) {
|
||||||
addJump(mth, insnByOffset, offset, target);
|
addJump(mth, insnByOffset, offset, target);
|
||||||
@@ -79,8 +74,10 @@ public class ProcessInstructionsVisitor extends AbstractVisitor {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case INVOKE:
|
case INVOKE:
|
||||||
ArgType retType = ((BaseInvokeNode) insn).getCallMth().getReturnType();
|
if (insn.getResult() == null) {
|
||||||
mergeMoveResult(insnByOffset, offset, insn, retType);
|
ArgType retType = ((BaseInvokeNode) insn).getCallMth().getReturnType();
|
||||||
|
mergeMoveResult(insnByOffset, offset, insn, retType);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case FILLED_NEW_ARRAY:
|
case FILLED_NEW_ARRAY:
|
||||||
@@ -105,6 +102,19 @@ public class ProcessInstructionsVisitor extends AbstractVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void attachSwitchData(InsnNode[] insnByOffset, int offset, SwitchInsn sw) {
|
||||||
|
int nextInsnOffset = getNextInsnOffset(insnByOffset, offset);
|
||||||
|
int dataTarget = sw.getDataTarget();
|
||||||
|
InsnNode switchDataInsn = getInsnAtOffset(insnByOffset, dataTarget);
|
||||||
|
if (switchDataInsn != null && switchDataInsn.getType() == InsnType.SWITCH_DATA) {
|
||||||
|
SwitchData data = (SwitchData) switchDataInsn;
|
||||||
|
data.fixTargets(offset);
|
||||||
|
sw.attachSwitchData(data, nextInsnOffset);
|
||||||
|
} else {
|
||||||
|
throw new JadxRuntimeException("Payload for switch not found at " + InsnUtils.formatOffset(dataTarget));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void mergeMoveResult(InsnNode[] insnByOffset, int offset, InsnNode insn, ArgType resType) {
|
private static void mergeMoveResult(InsnNode[] insnByOffset, int offset, InsnNode insn, ArgType resType) {
|
||||||
int nextInsnOffset = getNextInsnOffset(insnByOffset, offset);
|
int nextInsnOffset = getNextInsnOffset(insnByOffset, offset);
|
||||||
if (nextInsnOffset == -1) {
|
if (nextInsnOffset == -1) {
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
package jadx.core.dex.visitors;
|
package jadx.core.dex.visitors;
|
||||||
|
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
@@ -21,7 +24,6 @@ import jadx.core.dex.instructions.args.InsnArg;
|
|||||||
import jadx.core.dex.instructions.args.InsnWrapArg;
|
import jadx.core.dex.instructions.args.InsnWrapArg;
|
||||||
import jadx.core.dex.instructions.args.LiteralArg;
|
import jadx.core.dex.instructions.args.LiteralArg;
|
||||||
import jadx.core.dex.instructions.args.RegisterArg;
|
import jadx.core.dex.instructions.args.RegisterArg;
|
||||||
import jadx.core.dex.instructions.args.SSAVar;
|
|
||||||
import jadx.core.dex.nodes.BlockNode;
|
import jadx.core.dex.nodes.BlockNode;
|
||||||
import jadx.core.dex.nodes.ClassNode;
|
import jadx.core.dex.nodes.ClassNode;
|
||||||
import jadx.core.dex.nodes.FieldNode;
|
import jadx.core.dex.nodes.FieldNode;
|
||||||
@@ -53,19 +55,28 @@ public class ReSugarCode extends AbstractVisitor {
|
|||||||
if (mth.isNoCode()) {
|
if (mth.isNoCode()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
boolean changed = false;
|
int k = 0;
|
||||||
InsnRemover remover = new InsnRemover(mth);
|
while (true) {
|
||||||
for (BlockNode block : mth.getBasicBlocks()) {
|
boolean changed = false;
|
||||||
remover.setBlock(block);
|
InsnRemover remover = new InsnRemover(mth);
|
||||||
List<InsnNode> instructions = block.getInstructions();
|
for (BlockNode block : mth.getBasicBlocks()) {
|
||||||
int size = instructions.size();
|
remover.setBlock(block);
|
||||||
for (int i = 0; i < size; i++) {
|
List<InsnNode> instructions = block.getInstructions();
|
||||||
changed |= process(mth, instructions, i, remover);
|
int size = instructions.size();
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
changed |= process(mth, instructions, i, remover);
|
||||||
|
}
|
||||||
|
remover.perform();
|
||||||
|
}
|
||||||
|
if (changed) {
|
||||||
|
CodeShrinkVisitor.shrinkMethod(mth);
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (k++ > 100) {
|
||||||
|
mth.addWarnComment("Reached limit for ReSugarCode iterations");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
remover.perform();
|
|
||||||
}
|
|
||||||
if (changed) {
|
|
||||||
CodeShrinkVisitor.shrinkMethod(mth);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,7 +87,7 @@ public class ReSugarCode extends AbstractVisitor {
|
|||||||
}
|
}
|
||||||
switch (insn.getType()) {
|
switch (insn.getType()) {
|
||||||
case NEW_ARRAY:
|
case NEW_ARRAY:
|
||||||
return processNewArray(mth, (NewArrayNode) insn, instructions, remover);
|
return processNewArray(mth, (NewArrayNode) insn, instructions, i, remover);
|
||||||
|
|
||||||
case SWITCH:
|
case SWITCH:
|
||||||
return processEnumSwitch(mth, (SwitchInsn) insn);
|
return processEnumSwitch(mth, (SwitchInsn) insn);
|
||||||
@@ -90,7 +101,7 @@ public class ReSugarCode extends AbstractVisitor {
|
|||||||
* Replace new-array and sequence of array-put to new filled-array instruction.
|
* Replace new-array and sequence of array-put to new filled-array instruction.
|
||||||
*/
|
*/
|
||||||
private static boolean processNewArray(MethodNode mth, NewArrayNode newArrayInsn,
|
private static boolean processNewArray(MethodNode mth, NewArrayNode newArrayInsn,
|
||||||
List<InsnNode> instructions, InsnRemover remover) {
|
List<InsnNode> instructions, int i, InsnRemover remover) {
|
||||||
Object arrayLenConst = InsnUtils.getConstValueByArg(mth.root(), newArrayInsn.getArg(0));
|
Object arrayLenConst = InsnUtils.getConstValueByArg(mth.root(), newArrayInsn.getArg(0));
|
||||||
if (!(arrayLenConst instanceof LiteralArg)) {
|
if (!(arrayLenConst instanceof LiteralArg)) {
|
||||||
return false;
|
return false;
|
||||||
@@ -100,30 +111,33 @@ public class ReSugarCode extends AbstractVisitor {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
RegisterArg arrArg = newArrayInsn.getResult();
|
RegisterArg arrArg = newArrayInsn.getResult();
|
||||||
SSAVar ssaVar = arrArg.getSVar();
|
List<RegisterArg> useList = arrArg.getSVar().getUseList();
|
||||||
List<RegisterArg> useList = ssaVar.getUseList();
|
|
||||||
if (useList.size() < len) {
|
if (useList.size() < len) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// check sequential array put with increasing index
|
List<InsnNode> arrPuts = useList.stream()
|
||||||
int putIndex = 0;
|
.map(InsnArg::getParentInsn)
|
||||||
for (RegisterArg useArg : useList) {
|
.filter(Objects::nonNull)
|
||||||
InsnNode insn = useArg.getParentInsn();
|
.filter(insn -> insn.getType() == InsnType.APUT)
|
||||||
if (checkPutInsn(mth, insn, arrArg, putIndex)) {
|
.sorted(Comparator.comparingLong(insn -> {
|
||||||
putIndex++;
|
Object constVal = InsnUtils.getConstValueByArg(mth.root(), insn.getArg(1));
|
||||||
} else {
|
if (constVal instanceof LiteralArg) {
|
||||||
break;
|
return ((LiteralArg) constVal).getLiteral();
|
||||||
}
|
}
|
||||||
}
|
return -1; // bad value, put at top to fail fast next check
|
||||||
if (putIndex != len) {
|
}))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
if (arrPuts.size() != len) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
List<InsnNode> arrPuts = useList.subList(0, len).stream().map(InsnArg::getParentInsn).collect(Collectors.toList());
|
// expect all puts to be in same block
|
||||||
// check that all puts in current block
|
if (!new HashSet<>(instructions).containsAll(arrPuts)) {
|
||||||
for (InsnNode arrPut : arrPuts) {
|
return false;
|
||||||
int index = InsnList.getIndex(instructions, arrPut);
|
}
|
||||||
if (index == -1) {
|
|
||||||
mth.addDebugComment("Can't convert new array creation: APUT found in different block: " + arrPut);
|
for (int j = 0; j < len; j++) {
|
||||||
|
InsnNode insn = arrPuts.get(j);
|
||||||
|
if (!checkPutInsn(mth, insn, arrArg, j)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,7 +148,7 @@ public class ReSugarCode extends AbstractVisitor {
|
|||||||
filledArr.setResult(arrArg.duplicate());
|
filledArr.setResult(arrArg.duplicate());
|
||||||
|
|
||||||
for (InsnNode put : arrPuts) {
|
for (InsnNode put : arrPuts) {
|
||||||
filledArr.addArg(put.getArg(2).duplicate());
|
filledArr.addArg(replaceConstInArg(mth, put.getArg(2)));
|
||||||
remover.addAndUnbind(put);
|
remover.addAndUnbind(put);
|
||||||
}
|
}
|
||||||
remover.addAndUnbind(newArrayInsn);
|
remover.addAndUnbind(newArrayInsn);
|
||||||
@@ -145,6 +159,17 @@ public class ReSugarCode extends AbstractVisitor {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static InsnArg replaceConstInArg(MethodNode mth, InsnArg valueArg) {
|
||||||
|
if (valueArg.isLiteral()) {
|
||||||
|
FieldNode f = mth.getParentClass().getConstFieldByLiteralArg((LiteralArg) valueArg);
|
||||||
|
if (f != null) {
|
||||||
|
InsnNode fGet = new IndexInsnNode(InsnType.SGET, f.getFieldInfo(), 0);
|
||||||
|
return InsnArg.wrapArg(fGet);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return valueArg.duplicate();
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean checkPutInsn(MethodNode mth, InsnNode insn, RegisterArg arrArg, int putIndex) {
|
private static boolean checkPutInsn(MethodNode mth, InsnNode insn, RegisterArg arrArg, int putIndex) {
|
||||||
if (insn == null || insn.getType() != InsnType.APUT) {
|
if (insn == null || insn.getType() != InsnType.APUT) {
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -406,7 +406,7 @@ public class SimplifyVisitor extends AbstractVisitor {
|
|||||||
checkResult(mth, concatInsn);
|
checkResult(mth, concatInsn);
|
||||||
return concatInsn;
|
return concatInsn;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
LOG.warn("Can't convert string concatenation: {} insn: {}", mth, toStrInsn, e);
|
mth.addWarnComment("String concatenation convert failed", e);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import jadx.core.dex.attributes.nodes.JumpInfo;
|
|||||||
import jadx.core.dex.instructions.IfNode;
|
import jadx.core.dex.instructions.IfNode;
|
||||||
import jadx.core.dex.instructions.InsnType;
|
import jadx.core.dex.instructions.InsnType;
|
||||||
import jadx.core.dex.instructions.TargetInsnNode;
|
import jadx.core.dex.instructions.TargetInsnNode;
|
||||||
|
import jadx.core.dex.instructions.args.RegisterArg;
|
||||||
import jadx.core.dex.nodes.BlockNode;
|
import jadx.core.dex.nodes.BlockNode;
|
||||||
import jadx.core.dex.nodes.InsnNode;
|
import jadx.core.dex.nodes.InsnNode;
|
||||||
import jadx.core.dex.nodes.MethodNode;
|
import jadx.core.dex.nodes.MethodNode;
|
||||||
@@ -52,6 +53,7 @@ public class BlockSplitter extends AbstractVisitor {
|
|||||||
splitBasicBlocks(mth);
|
splitBasicBlocks(mth);
|
||||||
initBlocksInTargetNodes(mth);
|
initBlocksInTargetNodes(mth);
|
||||||
|
|
||||||
|
expandMoveMulti(mth);
|
||||||
removeJumpAttr(mth);
|
removeJumpAttr(mth);
|
||||||
removeInsns(mth);
|
removeInsns(mth);
|
||||||
removeEmptyDetachedBlocks(mth);
|
removeEmptyDetachedBlocks(mth);
|
||||||
@@ -308,6 +310,34 @@ public class BlockSplitter extends AbstractVisitor {
|
|||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void expandMoveMulti(MethodNode mth) {
|
||||||
|
for (BlockNode block : mth.getBasicBlocks()) {
|
||||||
|
List<InsnNode> insnsList = block.getInstructions();
|
||||||
|
int len = insnsList.size();
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
InsnNode insn = insnsList.get(i);
|
||||||
|
if (insn.getType() == InsnType.MOVE_MULTI) {
|
||||||
|
int mvCount = insn.getArgsCount() / 2;
|
||||||
|
for (int j = 0; j < mvCount; j++) {
|
||||||
|
InsnNode mv = new InsnNode(InsnType.MOVE, 1);
|
||||||
|
int startArg = j * 2;
|
||||||
|
mv.setResult((RegisterArg) insn.getArg(startArg));
|
||||||
|
mv.addArg(insn.getArg(startArg + 1));
|
||||||
|
mv.copyAttributesFrom(insn);
|
||||||
|
if (j == 0) {
|
||||||
|
mv.setOffset(insn.getOffset());
|
||||||
|
insnsList.set(i, mv);
|
||||||
|
} else {
|
||||||
|
insnsList.add(i + j, mv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
i += mvCount - 1;
|
||||||
|
len = insnsList.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void removeJumpAttr(MethodNode mth) {
|
private static void removeJumpAttr(MethodNode mth) {
|
||||||
for (BlockNode block : mth.getBasicBlocks()) {
|
for (BlockNode block : mth.getBasicBlocks()) {
|
||||||
for (InsnNode insn : block.getInstructions()) {
|
for (InsnNode insn : block.getInstructions()) {
|
||||||
@@ -403,7 +433,6 @@ public class BlockSplitter extends AbstractVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+14
-8
@@ -53,18 +53,24 @@ public class DebugInfoAttachVisitor extends AbstractVisitor {
|
|||||||
|
|
||||||
private void processDebugInfo(MethodNode mth, IDebugInfo debugInfo) {
|
private void processDebugInfo(MethodNode mth, IDebugInfo debugInfo) {
|
||||||
InsnNode[] insnArr = mth.getInstructions();
|
InsnNode[] insnArr = mth.getInstructions();
|
||||||
attachSourceLines(debugInfo.getSourceLineMapping(), insnArr);
|
attachSourceLines(mth, debugInfo.getSourceLineMapping(), insnArr);
|
||||||
attachDebugInfo(mth, debugInfo.getLocalVars(), insnArr);
|
attachDebugInfo(mth, debugInfo.getLocalVars(), insnArr);
|
||||||
setMethodSourceLine(mth, insnArr);
|
setMethodSourceLine(mth, insnArr);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void attachSourceLines(Map<Integer, Integer> lineMapping, InsnNode[] insnArr) {
|
private void attachSourceLines(MethodNode mth, Map<Integer, Integer> lineMapping, InsnNode[] insnArr) {
|
||||||
for (InsnNode insn : insnArr) {
|
if (lineMapping.isEmpty()) {
|
||||||
if (insn != null) {
|
return;
|
||||||
Integer sourceLine = lineMapping.get(insn.getOffset());
|
}
|
||||||
if (sourceLine != null) {
|
for (Map.Entry<Integer, Integer> entry : lineMapping.entrySet()) {
|
||||||
insn.setSourceLine(sourceLine);
|
try {
|
||||||
|
Integer offset = entry.getKey();
|
||||||
|
InsnNode insn = insnArr[offset];
|
||||||
|
if (insn != null) {
|
||||||
|
insn.setSourceLine(entry.getValue());
|
||||||
}
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
mth.addWarnComment("Error attach source line", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -80,7 +86,7 @@ public class DebugInfoAttachVisitor extends AbstractVisitor {
|
|||||||
|
|
||||||
ArgType type = getVarType(mth, var);
|
ArgType type = getVarType(mth, var);
|
||||||
RegDebugInfoAttr debugInfoAttr = new RegDebugInfoAttr(type, var.getName());
|
RegDebugInfoAttr debugInfoAttr = new RegDebugInfoAttr(type, var.getName());
|
||||||
if (start < 0) {
|
if (start <= 0) {
|
||||||
// attach to method arguments
|
// attach to method arguments
|
||||||
RegisterArg thisArg = mth.getThisArg();
|
RegisterArg thisArg = mth.getThisArg();
|
||||||
if (thisArg != null) {
|
if (thisArg != null) {
|
||||||
|
|||||||
@@ -171,13 +171,19 @@ public class LoopRegionVisitor extends AbstractVisitor implements IRegionVisitor
|
|||||||
}
|
}
|
||||||
SSAVar sVar = ((RegisterArg) condArg).getSVar();
|
SSAVar sVar = ((RegisterArg) condArg).getSVar();
|
||||||
List<RegisterArg> args = sVar.getUseList();
|
List<RegisterArg> args = sVar.getUseList();
|
||||||
if (args.size() != 3 || args.get(2) != condArg) {
|
if (args.size() != 3) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
condArg = InsnUtils.getRegFromInsn(args, InsnType.IF);
|
||||||
|
if (condArg == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
RegisterArg arrIndex = InsnUtils.getRegFromInsn(args, InsnType.AGET);
|
||||||
|
if (arrIndex == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
condArg = args.get(0);
|
|
||||||
RegisterArg arrIndex = args.get(1);
|
|
||||||
InsnNode arrGetInsn = arrIndex.getParentInsn();
|
InsnNode arrGetInsn = arrIndex.getParentInsn();
|
||||||
if (arrGetInsn == null || arrGetInsn.getType() != InsnType.AGET || arrGetInsn.containsWrappedInsn()) {
|
if (arrGetInsn == null || arrGetInsn.containsWrappedInsn()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (!condition.isCompare()) {
|
if (!condition.isCompare()) {
|
||||||
|
|||||||
@@ -106,7 +106,6 @@ public class TernaryMod implements IRegionIterativeVisitor {
|
|||||||
InsnArg thenArg = InsnArg.wrapInsnIntoArg(thenInsn);
|
InsnArg thenArg = InsnArg.wrapInsnIntoArg(thenInsn);
|
||||||
InsnArg elseArg = InsnArg.wrapInsnIntoArg(elseInsn);
|
InsnArg elseArg = InsnArg.wrapInsnIntoArg(elseInsn);
|
||||||
TernaryInsn ternInsn = new TernaryInsn(ifRegion.getCondition(), resArg, thenArg, elseArg);
|
TernaryInsn ternInsn = new TernaryInsn(ifRegion.getCondition(), resArg, thenArg, elseArg);
|
||||||
ternInsn.setSourceLine(thenInsn.getSourceLine());
|
|
||||||
|
|
||||||
InsnRemover.unbindResult(mth, elseInsn);
|
InsnRemover.unbindResult(mth, elseInsn);
|
||||||
|
|
||||||
@@ -141,7 +140,6 @@ public class TernaryMod implements IRegionIterativeVisitor {
|
|||||||
eb.remove(AFlag.RETURN);
|
eb.remove(AFlag.RETURN);
|
||||||
|
|
||||||
TernaryInsn ternInsn = new TernaryInsn(ifRegion.getCondition(), null, thenArg, elseArg);
|
TernaryInsn ternInsn = new TernaryInsn(ifRegion.getCondition(), null, thenArg, elseArg);
|
||||||
ternInsn.setSourceLine(thenInsn.getSourceLine());
|
|
||||||
InsnNode retInsn = new InsnNode(InsnType.RETURN, 1);
|
InsnNode retInsn = new InsnNode(InsnType.RETURN, 1);
|
||||||
InsnArg arg = InsnArg.wrapInsnIntoArg(ternInsn);
|
InsnArg arg = InsnArg.wrapInsnIntoArg(ternInsn);
|
||||||
arg.setType(thenArg.getType());
|
arg.setType(thenArg.getType());
|
||||||
@@ -281,7 +279,6 @@ public class TernaryMod implements IRegionIterativeVisitor {
|
|||||||
InsnList.remove(block, insn);
|
InsnList.remove(block, insn);
|
||||||
TernaryInsn ternInsn = new TernaryInsn(ifRegion.getCondition(),
|
TernaryInsn ternInsn = new TernaryInsn(ifRegion.getCondition(),
|
||||||
phiInsn.getResult(), InsnArg.wrapInsnIntoArg(insn), otherArg);
|
phiInsn.getResult(), InsnArg.wrapInsnIntoArg(insn), otherArg);
|
||||||
ternInsn.setSourceLine(insn.getSourceLine());
|
|
||||||
|
|
||||||
InsnRemover.unbindAllArgs(mth, phiInsn);
|
InsnRemover.unbindAllArgs(mth, phiInsn);
|
||||||
header.getInstructions().clear();
|
header.getInstructions().clear();
|
||||||
|
|||||||
@@ -6,10 +6,7 @@ import java.util.List;
|
|||||||
import java.util.ListIterator;
|
import java.util.ListIterator;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import jadx.core.dex.attributes.AFlag;
|
import jadx.core.dex.attributes.AFlag;
|
||||||
import jadx.core.dex.attributes.AType;
|
|
||||||
import jadx.core.dex.instructions.InsnType;
|
import jadx.core.dex.instructions.InsnType;
|
||||||
import jadx.core.dex.instructions.args.InsnArg;
|
import jadx.core.dex.instructions.args.InsnArg;
|
||||||
import jadx.core.dex.instructions.args.InsnWrapArg;
|
import jadx.core.dex.instructions.args.InsnWrapArg;
|
||||||
@@ -152,27 +149,15 @@ public class CodeShrinkVisitor extends AbstractVisitor {
|
|||||||
InsnArg wrappedArg = arg.wrapInstruction(mth, insn, false);
|
InsnArg wrappedArg = arg.wrapInstruction(mth, insn, false);
|
||||||
boolean replaced = wrappedArg != null;
|
boolean replaced = wrappedArg != null;
|
||||||
if (replaced) {
|
if (replaced) {
|
||||||
processCodeComment(insn, arg.getParentInsn());
|
InsnNode parentInsn = arg.getParentInsn();
|
||||||
|
if (parentInsn != null) {
|
||||||
|
parentInsn.inheritMetadata(insn);
|
||||||
|
}
|
||||||
InsnRemover.removeWithoutUnbind(mth, block, insn);
|
InsnRemover.removeWithoutUnbind(mth, block, insn);
|
||||||
}
|
}
|
||||||
return replaced;
|
return replaced;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void processCodeComment(InsnNode insn, @Nullable InsnNode parentInsn) {
|
|
||||||
if (parentInsn == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (parentInsn.getType() == InsnType.RETURN) {
|
|
||||||
parentInsn.setSourceLine(insn.getSourceLine());
|
|
||||||
if (parentInsn.contains(AFlag.SYNTHETIC)) {
|
|
||||||
parentInsn.setOffset(insn.getOffset());
|
|
||||||
parentInsn.rewriteAttributeFrom(insn, AType.CODE_COMMENTS);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
parentInsn.copyAttributeFrom(insn, AType.CODE_COMMENTS);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean canMoveBetweenBlocks(MethodNode mth, InsnNode assignInsn, BlockNode assignBlock,
|
private static boolean canMoveBetweenBlocks(MethodNode mth, InsnNode assignInsn, BlockNode assignBlock,
|
||||||
BlockNode useBlock, InsnNode useInsn) {
|
BlockNode useBlock, InsnNode useInsn) {
|
||||||
if (!BlockUtils.isPathExists(assignBlock, useBlock)) {
|
if (!BlockUtils.isPathExists(assignBlock, useBlock)) {
|
||||||
@@ -238,6 +223,7 @@ public class CodeShrinkVisitor extends AbstractVisitor {
|
|||||||
InsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn();
|
InsnNode wrapInsn = ((InsnWrapArg) arg).getWrapInsn();
|
||||||
wrapInsn.setResult(insn.getResult());
|
wrapInsn.setResult(insn.getResult());
|
||||||
wrapInsn.copyAttributesFrom(insn);
|
wrapInsn.copyAttributesFrom(insn);
|
||||||
|
wrapInsn.addSourceLineFrom(insn);
|
||||||
wrapInsn.setOffset(insn.getOffset());
|
wrapInsn.setOffset(insn.getOffset());
|
||||||
wrapInsn.remove(AFlag.WRAPPED);
|
wrapInsn.remove(AFlag.WRAPPED);
|
||||||
block.getInstructions().set(i, wrapInsn);
|
block.getInstructions().set(i, wrapInsn);
|
||||||
|
|||||||
@@ -73,6 +73,7 @@ public class SSATransform extends AbstractVisitor {
|
|||||||
} while (repeatFix);
|
} while (repeatFix);
|
||||||
|
|
||||||
hidePhiInsns(mth);
|
hidePhiInsns(mth);
|
||||||
|
removeUnusedInvokeResults(mth);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void placePhi(MethodNode mth, int regNum, LiveVarAnalysis la) {
|
private static void placePhi(MethodNode mth, int regNum, LiveVarAnalysis la) {
|
||||||
@@ -449,4 +450,18 @@ public class SSATransform extends AbstractVisitor {
|
|||||||
}
|
}
|
||||||
mth.getSVars().clear();
|
mth.getSVars().clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void removeUnusedInvokeResults(MethodNode mth) {
|
||||||
|
Iterator<SSAVar> it = mth.getSVars().iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
SSAVar ssaVar = it.next();
|
||||||
|
if (ssaVar.getUseCount() == 0) {
|
||||||
|
InsnNode parentInsn = ssaVar.getAssign().getParentInsn();
|
||||||
|
if (parentInsn != null && parentInsn.getType() == InsnType.INVOKE) {
|
||||||
|
parentInsn.setResult(null);
|
||||||
|
it.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
package jadx.core.dex.visitors.usage;
|
package jadx.core.dex.visitors.usage;
|
||||||
|
|
||||||
|
import jadx.api.plugins.input.data.ICallSite;
|
||||||
import jadx.api.plugins.input.data.ICodeReader;
|
import jadx.api.plugins.input.data.ICodeReader;
|
||||||
|
import jadx.api.plugins.input.data.IMethodHandle;
|
||||||
|
import jadx.api.plugins.input.data.IMethodRef;
|
||||||
import jadx.api.plugins.input.insns.InsnData;
|
import jadx.api.plugins.input.insns.InsnData;
|
||||||
import jadx.api.plugins.input.insns.Opcode;
|
import jadx.api.plugins.input.insns.Opcode;
|
||||||
|
import jadx.api.plugins.input.insns.custom.ICustomPayload;
|
||||||
import jadx.core.dex.info.FieldInfo;
|
import jadx.core.dex.info.FieldInfo;
|
||||||
import jadx.core.dex.info.MethodInfo;
|
import jadx.core.dex.info.MethodInfo;
|
||||||
import jadx.core.dex.instructions.args.ArgType;
|
import jadx.core.dex.instructions.args.ArgType;
|
||||||
@@ -92,19 +96,45 @@ public class UsageInfoVisitor extends AbstractVisitor {
|
|||||||
|
|
||||||
case FIELD_REF:
|
case FIELD_REF:
|
||||||
insnData.decode();
|
insnData.decode();
|
||||||
FieldNode fieldNode = root.resolveField(FieldInfo.fromData(root, insnData.getIndexAsField()));
|
FieldNode fieldNode = root.resolveField(FieldInfo.fromRef(root, insnData.getIndexAsField()));
|
||||||
if (fieldNode != null) {
|
if (fieldNode != null) {
|
||||||
usageInfo.fieldUse(mth, fieldNode);
|
usageInfo.fieldUse(mth, fieldNode);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case METHOD_REF:
|
case METHOD_REF: {
|
||||||
insnData.decode();
|
insnData.decode();
|
||||||
MethodNode methodNode = root.resolveMethod(MethodInfo.fromRef(root, insnData.getIndexAsMethod()));
|
IMethodRef mthRef;
|
||||||
|
ICustomPayload payload = insnData.getPayload();
|
||||||
|
if (payload != null) {
|
||||||
|
mthRef = ((IMethodRef) payload);
|
||||||
|
} else {
|
||||||
|
mthRef = insnData.getIndexAsMethod();
|
||||||
|
}
|
||||||
|
MethodNode methodNode = root.resolveMethod(MethodInfo.fromRef(root, mthRef));
|
||||||
if (methodNode != null) {
|
if (methodNode != null) {
|
||||||
usageInfo.methodUse(mth, methodNode);
|
usageInfo.methodUse(mth, methodNode);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case CALL_SITE: {
|
||||||
|
insnData.decode();
|
||||||
|
ICallSite callSite;
|
||||||
|
ICustomPayload payload = insnData.getPayload();
|
||||||
|
if (payload != null) {
|
||||||
|
callSite = ((ICallSite) payload);
|
||||||
|
} else {
|
||||||
|
callSite = insnData.getIndexAsCallSite();
|
||||||
|
}
|
||||||
|
IMethodHandle methodHandle = (IMethodHandle) callSite.getValues().get(4).getValue();
|
||||||
|
IMethodRef mthRef = methodHandle.getMethodRef();
|
||||||
|
MethodNode mthNode = root.resolveMethod(MethodInfo.fromRef(root, mthRef));
|
||||||
|
if (mthNode != null) {
|
||||||
|
usageInfo.methodUse(mth, mthNode);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -641,7 +641,7 @@ public class BlockUtils {
|
|||||||
public static void replaceInsn(MethodNode mth, BlockNode block, int i, InsnNode insn) {
|
public static void replaceInsn(MethodNode mth, BlockNode block, int i, InsnNode insn) {
|
||||||
InsnNode prevInsn = block.getInstructions().get(i);
|
InsnNode prevInsn = block.getInstructions().get(i);
|
||||||
insn.copyAttributesFrom(prevInsn);
|
insn.copyAttributesFrom(prevInsn);
|
||||||
insn.setSourceLine(prevInsn.getSourceLine());
|
insn.inheritMetadata(prevInsn);
|
||||||
insn.setOffset(prevInsn.getOffset());
|
insn.setOffset(prevInsn.getOffset());
|
||||||
block.getInstructions().set(i, insn);
|
block.getInstructions().set(i, insn);
|
||||||
|
|
||||||
|
|||||||
@@ -6,11 +6,12 @@ import org.jetbrains.annotations.Nullable;
|
|||||||
|
|
||||||
import jadx.api.CodePosition;
|
import jadx.api.CodePosition;
|
||||||
import jadx.api.ICodeWriter;
|
import jadx.api.ICodeWriter;
|
||||||
|
import jadx.api.plugins.input.data.attributes.JadxAttrType;
|
||||||
|
import jadx.api.plugins.input.data.attributes.types.SourceFileAttr;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.AttrNode;
|
import jadx.core.dex.attributes.AttrNode;
|
||||||
import jadx.core.dex.attributes.IAttributeNode;
|
import jadx.core.dex.attributes.IAttributeNode;
|
||||||
import jadx.core.dex.attributes.nodes.RenameReasonAttr;
|
import jadx.core.dex.attributes.nodes.RenameReasonAttr;
|
||||||
import jadx.core.dex.attributes.nodes.SourceFileAttr;
|
|
||||||
import jadx.core.dex.instructions.args.CodeVar;
|
import jadx.core.dex.instructions.args.CodeVar;
|
||||||
import jadx.core.dex.instructions.args.RegisterArg;
|
import jadx.core.dex.instructions.args.RegisterArg;
|
||||||
import jadx.core.dex.instructions.args.SSAVar;
|
import jadx.core.dex.instructions.args.SSAVar;
|
||||||
@@ -88,7 +89,7 @@ public class CodeGenUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void addSourceFileInfo(ICodeWriter code, ClassNode node) {
|
public static void addSourceFileInfo(ICodeWriter code, ClassNode node) {
|
||||||
SourceFileAttr sourceFileAttr = node.get(AType.SOURCE_FILE);
|
SourceFileAttr sourceFileAttr = node.get(JadxAttrType.SOURCE_FILE);
|
||||||
if (sourceFileAttr != null) {
|
if (sourceFileAttr != null) {
|
||||||
String fileName = sourceFileAttr.getFileName();
|
String fileName = sourceFileAttr.getFileName();
|
||||||
String topClsName = node.getTopParentClass().getClassInfo().getShortName();
|
String topClsName = node.getTopParentClass().getClassInfo().getShortName();
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import jadx.api.plugins.input.data.annotations.EncodedValue;
|
|||||||
import jadx.core.dex.instructions.args.ArgType;
|
import jadx.core.dex.instructions.args.ArgType;
|
||||||
import jadx.core.dex.instructions.args.InsnArg;
|
import jadx.core.dex.instructions.args.InsnArg;
|
||||||
import jadx.core.dex.instructions.args.LiteralArg;
|
import jadx.core.dex.instructions.args.LiteralArg;
|
||||||
import jadx.core.dex.nodes.RootNode;
|
|
||||||
|
|
||||||
public class EncodedValueUtils {
|
public class EncodedValueUtils {
|
||||||
|
|
||||||
@@ -16,7 +15,7 @@ public class EncodedValueUtils {
|
|||||||
* @return LiteralArg, String, ArgType or null
|
* @return LiteralArg, String, ArgType or null
|
||||||
*/
|
*/
|
||||||
@Nullable
|
@Nullable
|
||||||
public static Object convertToConstValue(RootNode root, EncodedValue encodedValue) {
|
public static Object convertToConstValue(EncodedValue encodedValue) {
|
||||||
if (encodedValue == null) {
|
if (encodedValue == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,15 @@
|
|||||||
package jadx.core.utils;
|
package jadx.core.utils;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import jadx.api.plugins.input.data.annotations.EncodedValue;
|
||||||
|
import jadx.api.plugins.input.data.attributes.JadxAttrType;
|
||||||
import jadx.core.dex.attributes.AFlag;
|
import jadx.core.dex.attributes.AFlag;
|
||||||
import jadx.core.dex.attributes.AType;
|
|
||||||
import jadx.core.dex.attributes.fldinit.FieldInitAttr;
|
|
||||||
import jadx.core.dex.info.FieldInfo;
|
import jadx.core.dex.info.FieldInfo;
|
||||||
import jadx.core.dex.instructions.ConstClassNode;
|
import jadx.core.dex.instructions.ConstClassNode;
|
||||||
import jadx.core.dex.instructions.ConstStringNode;
|
import jadx.core.dex.instructions.ConstStringNode;
|
||||||
@@ -100,9 +101,9 @@ public class InsnUtils {
|
|||||||
LOG.warn("Field {} not found", f);
|
LOG.warn("Field {} not found", f);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
FieldInitAttr attr = fieldNode.get(AType.FIELD_INIT);
|
EncodedValue constVal = fieldNode.get(JadxAttrType.CONSTANT_VALUE);
|
||||||
if (attr != null && attr.isConst()) {
|
if (constVal != null) {
|
||||||
return EncodedValueUtils.convertToConstValue(root, attr.getEncodedValue());
|
return EncodedValueUtils.convertToConstValue(constVal);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@@ -139,6 +140,17 @@ public class InsnUtils {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static RegisterArg getRegFromInsn(List<RegisterArg> regs, InsnType insnType) {
|
||||||
|
for (RegisterArg reg : regs) {
|
||||||
|
InsnNode parentInsn = reg.getParentInsn();
|
||||||
|
if (parentInsn != null && parentInsn.getType() == insnType) {
|
||||||
|
return reg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private static InsnNode recursiveInsnCheck(InsnNode insn, InsnType insnType, Predicate<InsnNode> test) {
|
private static InsnNode recursiveInsnCheck(InsnNode insn, InsnType insnType, Predicate<InsnNode> test) {
|
||||||
if (insn.getType() == insnType && test.test(insn)) {
|
if (insn.getType() == insnType && test.test(insn)) {
|
||||||
return insn;
|
return insn;
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import jadx.core.codegen.ClassGen;
|
|||||||
import jadx.core.deobf.NameMapper;
|
import jadx.core.deobf.NameMapper;
|
||||||
import jadx.core.dex.attributes.AFlag;
|
import jadx.core.dex.attributes.AFlag;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.fldinit.FieldInitAttr;
|
|
||||||
import jadx.core.dex.info.AccessInfo;
|
import jadx.core.dex.info.AccessInfo;
|
||||||
import jadx.core.dex.info.ClassInfo;
|
import jadx.core.dex.info.ClassInfo;
|
||||||
import jadx.core.dex.info.ConstStorage;
|
import jadx.core.dex.info.ConstStorage;
|
||||||
@@ -77,10 +76,9 @@ public class AndroidResourcesUtils {
|
|||||||
/**
|
/**
|
||||||
* Force hex format for Android resources ids
|
* Force hex format for Android resources ids
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("RedundantCast")
|
public static boolean handleResourceFieldValue(ClassNode cls, ICodeWriter code, long lit, ArgType type) {
|
||||||
public static boolean handleResourceFieldValue(ClassNode cls, ICodeWriter code, EncodedValue encodedValue) {
|
if (type.equals(ArgType.INT) && isResourceClass(cls)) {
|
||||||
if (encodedValue.getType() == EncodedType.ENCODED_INT && isResourceClass(cls)) {
|
code.add(String.format("0x%08x", lit));
|
||||||
code.add(String.format("0x%08x", ((Integer) encodedValue.getValue())));
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -121,8 +119,7 @@ public class AndroidResourcesUtils {
|
|||||||
if (rField == null) {
|
if (rField == null) {
|
||||||
FieldInfo rFieldInfo = FieldInfo.from(typeCls.root(), typeCls.getClassInfo(), resName, ArgType.INT);
|
FieldInfo rFieldInfo = FieldInfo.from(typeCls.root(), typeCls.getClassInfo(), resName, ArgType.INT);
|
||||||
rField = new FieldNode(typeCls, rFieldInfo, AccessFlags.PUBLIC | AccessFlags.STATIC | AccessFlags.FINAL);
|
rField = new FieldNode(typeCls, rFieldInfo, AccessFlags.PUBLIC | AccessFlags.STATIC | AccessFlags.FINAL);
|
||||||
EncodedValue value = new EncodedValue(EncodedType.ENCODED_INT, resource.getId());
|
rField.addAttr(new EncodedValue(EncodedType.ENCODED_INT, resource.getId()));
|
||||||
rField.addAttr(FieldInitAttr.constValue(value));
|
|
||||||
typeCls.getFields().add(rField);
|
typeCls.getFields().add(rField);
|
||||||
if (rClsExists) {
|
if (rClsExists) {
|
||||||
rField.addAttr(AType.COMMENTS, "added by JADX");
|
rField.addAttr(AType.COMMENTS, "added by JADX");
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ public class FileUtils {
|
|||||||
|
|
||||||
public static Path createTempFileNoDelete(String suffix) {
|
public static Path createTempFileNoDelete(String suffix) {
|
||||||
try {
|
try {
|
||||||
return Files.createTempFile(TEMP_ROOT_DIR, JADX_TMP_PREFIX, suffix);
|
return Files.createTempFile(Files.createTempDirectory("jadx-persist"), "jadx-", suffix);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new JadxRuntimeException("Failed to create temp file with suffix: " + suffix, e);
|
throw new JadxRuntimeException("Failed to create temp file with suffix: " + suffix, e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
package jadx.tests.api;
|
package jadx.tests.api;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
import java.lang.reflect.InvocationTargetException;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -18,18 +16,21 @@ import java.util.concurrent.Executors;
|
|||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
import java.util.jar.JarOutputStream;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import jadx.api.CodePosition;
|
||||||
import jadx.api.ICodeInfo;
|
import jadx.api.ICodeInfo;
|
||||||
import jadx.api.ICodeWriter;
|
import jadx.api.ICodeWriter;
|
||||||
import jadx.api.JadxArgs;
|
import jadx.api.JadxArgs;
|
||||||
import jadx.api.JadxDecompiler;
|
import jadx.api.JadxDecompiler;
|
||||||
import jadx.api.JadxInternalAccess;
|
import jadx.api.JadxInternalAccess;
|
||||||
|
import jadx.api.data.annotations.InsnCodeOffset;
|
||||||
import jadx.core.dex.attributes.AFlag;
|
import jadx.core.dex.attributes.AFlag;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.AttrList;
|
import jadx.core.dex.attributes.AttrList;
|
||||||
@@ -47,7 +48,6 @@ import jadx.tests.api.compiler.DynamicCompiler;
|
|||||||
import jadx.tests.api.compiler.StaticCompiler;
|
import jadx.tests.api.compiler.StaticCompiler;
|
||||||
import jadx.tests.api.utils.TestUtils;
|
import jadx.tests.api.utils.TestUtils;
|
||||||
|
|
||||||
import static jadx.core.utils.files.FileUtils.addFileToJar;
|
|
||||||
import static org.apache.commons.lang3.StringUtils.leftPad;
|
import static org.apache.commons.lang3.StringUtils.leftPad;
|
||||||
import static org.apache.commons.lang3.StringUtils.rightPad;
|
import static org.apache.commons.lang3.StringUtils.rightPad;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
@@ -63,11 +63,18 @@ import static org.junit.jupiter.api.Assertions.fail;
|
|||||||
|
|
||||||
public abstract class IntegrationTest extends TestUtils {
|
public abstract class IntegrationTest extends TestUtils {
|
||||||
|
|
||||||
|
private static final Logger LOG = LoggerFactory.getLogger(IntegrationTest.class);
|
||||||
private static final String TEST_DIRECTORY = "src/test/java";
|
private static final String TEST_DIRECTORY = "src/test/java";
|
||||||
private static final String TEST_DIRECTORY2 = "jadx-core/" + TEST_DIRECTORY;
|
private static final String TEST_DIRECTORY2 = "jadx-core/" + TEST_DIRECTORY;
|
||||||
|
|
||||||
private static final String OUT_DIR = "test-out-tmp";
|
private static final String OUT_DIR = "test-out-tmp";
|
||||||
|
|
||||||
|
private static final String DEFAULT_INPUT_PLUGIN = "dx";
|
||||||
|
/**
|
||||||
|
* Set 'TEST_INPUT_PLUGIN' env variable to use 'java' or 'dx' input in tests
|
||||||
|
*/
|
||||||
|
private static final boolean USE_JAVA_INPUT = Utils.getOrElse(System.getenv("TEST_INPUT_PLUGIN"), DEFAULT_INPUT_PLUGIN).equals("java");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run auto check method if defined:
|
* Run auto check method if defined:
|
||||||
*
|
*
|
||||||
@@ -80,26 +87,20 @@ public abstract class IntegrationTest extends TestUtils {
|
|||||||
|
|
||||||
protected JadxArgs args;
|
protected JadxArgs args;
|
||||||
|
|
||||||
protected boolean deleteTmpFiles;
|
|
||||||
protected boolean withDebugInfo;
|
protected boolean withDebugInfo;
|
||||||
protected boolean unloadCls;
|
|
||||||
protected boolean compile;
|
protected boolean compile;
|
||||||
protected boolean useEclipseCompiler;
|
protected boolean useEclipseCompiler;
|
||||||
protected Map<Integer, String> resMap = Collections.emptyMap();
|
protected Map<Integer, String> resMap = Collections.emptyMap();
|
||||||
|
|
||||||
private boolean allowWarnInCode;
|
private boolean allowWarnInCode;
|
||||||
private boolean printLineNumbers;
|
private boolean printLineNumbers;
|
||||||
private boolean printSmali;
|
private boolean printOffsets;
|
||||||
|
private boolean printDisassemble;
|
||||||
|
private Boolean useJavaInput = null;
|
||||||
|
|
||||||
private DynamicCompiler dynamicCompiler;
|
private DynamicCompiler dynamicCompiler;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// needed for post decompile check
|
|
||||||
AType.SKIP_ON_UNLOAD.addAll(Arrays.asList(
|
|
||||||
AType.JADX_ERROR,
|
|
||||||
AType.JADX_WARN,
|
|
||||||
AType.COMMENTS));
|
|
||||||
|
|
||||||
// enable debug checks
|
// enable debug checks
|
||||||
DebugChecks.checksEnabled = true;
|
DebugChecks.checksEnabled = true;
|
||||||
}
|
}
|
||||||
@@ -108,8 +109,6 @@ public abstract class IntegrationTest extends TestUtils {
|
|||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void init() {
|
public void init() {
|
||||||
this.deleteTmpFiles = true;
|
|
||||||
this.unloadCls = true;
|
|
||||||
this.withDebugInfo = true;
|
this.withDebugInfo = true;
|
||||||
this.compile = true;
|
this.compile = true;
|
||||||
this.useEclipseCompiler = false;
|
this.useEclipseCompiler = false;
|
||||||
@@ -141,8 +140,9 @@ public abstract class IntegrationTest extends TestUtils {
|
|||||||
|
|
||||||
public ClassNode getClassNode(Class<?> clazz) {
|
public ClassNode getClassNode(Class<?> clazz) {
|
||||||
try {
|
try {
|
||||||
File jar = getJarForClass(clazz);
|
List<File> files = compileClass(clazz);
|
||||||
return getClassNodeFromFiles(Collections.singletonList(jar), clazz.getName());
|
assertThat("File list is empty", files, not(empty()));
|
||||||
|
return getClassNodeFromFiles(files, clazz.getName());
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
fail(e.getMessage());
|
fail(e.getMessage());
|
||||||
@@ -181,6 +181,13 @@ public abstract class IntegrationTest extends TestUtils {
|
|||||||
protected JadxDecompiler loadFiles(List<File> inputFiles) {
|
protected JadxDecompiler loadFiles(List<File> inputFiles) {
|
||||||
args.setInputFiles(inputFiles);
|
args.setInputFiles(inputFiles);
|
||||||
JadxDecompiler d = new JadxDecompiler(args);
|
JadxDecompiler d = new JadxDecompiler(args);
|
||||||
|
if (isJavaInput()) {
|
||||||
|
d.getPluginManager().unload("java-convert");
|
||||||
|
LOG.info("Using java input");
|
||||||
|
} else {
|
||||||
|
d.getPluginManager().unload("java-input");
|
||||||
|
LOG.info("Using dex input");
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
d.load();
|
d.load();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@@ -199,25 +206,24 @@ public abstract class IntegrationTest extends TestUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected void decompileAndCheck(List<ClassNode> clsList) {
|
protected void decompileAndCheck(List<ClassNode> clsList) {
|
||||||
if (!unloadCls) {
|
clsList.forEach(cls -> cls.add(AFlag.DONT_UNLOAD_CLASS)); // keep error and warning attributes
|
||||||
clsList.forEach(cls -> cls.add(AFlag.DONT_UNLOAD_CLASS));
|
|
||||||
}
|
|
||||||
clsList.forEach(ClassNode::decompile);
|
clsList.forEach(ClassNode::decompile);
|
||||||
|
|
||||||
for (ClassNode cls : clsList) {
|
for (ClassNode cls : clsList) {
|
||||||
System.out.println("-----------------------------------------------------------");
|
System.out.println("-----------------------------------------------------------");
|
||||||
|
ICodeInfo code = cls.getCode();
|
||||||
if (printLineNumbers) {
|
if (printLineNumbers) {
|
||||||
printCodeWithLineNumbers(cls.getCode());
|
printCodeWithLineNumbers(code);
|
||||||
|
} else if (printOffsets) {
|
||||||
|
printCodeWithOffsets(code);
|
||||||
} else {
|
} else {
|
||||||
System.out.println(cls.getCode());
|
System.out.println(code);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
System.out.println("-----------------------------------------------------------");
|
System.out.println("-----------------------------------------------------------");
|
||||||
|
if (printDisassemble) {
|
||||||
if (printSmali) {
|
|
||||||
clsList.forEach(this::printSmali);
|
clsList.forEach(this::printSmali);
|
||||||
}
|
}
|
||||||
|
|
||||||
runChecks(clsList);
|
runChecks(clsList);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,7 +239,7 @@ public abstract class IntegrationTest extends TestUtils {
|
|||||||
|
|
||||||
private void printSmali(ClassNode cls) {
|
private void printSmali(ClassNode cls) {
|
||||||
System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
|
System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
|
||||||
System.out.println(cls.getSmali());
|
System.out.println(cls.getDisassembledCode());
|
||||||
System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
|
System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -253,6 +259,23 @@ public abstract class IntegrationTest extends TestUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void printCodeWithOffsets(ICodeInfo code) {
|
||||||
|
String codeStr = code.getCodeStr();
|
||||||
|
Map<CodePosition, Object> annotations = code.getAnnotations();
|
||||||
|
String[] lines = codeStr.split(ICodeWriter.NL);
|
||||||
|
for (int i = 0; i < lines.length; i++) {
|
||||||
|
String line = lines[i];
|
||||||
|
int curLine = i + 1;
|
||||||
|
Object ann = annotations.get(new CodePosition(curLine, 0));
|
||||||
|
String offsetStr = "";
|
||||||
|
if (ann instanceof InsnCodeOffset) {
|
||||||
|
int offset = ((InsnCodeOffset) ann).getOffset();
|
||||||
|
offsetStr = "/* " + leftPad(String.valueOf(offset), 5) + " */";
|
||||||
|
}
|
||||||
|
System.out.println(rightPad(offsetStr, 12) + line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void insertResources(RootNode root) {
|
private void insertResources(RootNode root) {
|
||||||
if (resMap.isEmpty()) {
|
if (resMap.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
@@ -275,7 +298,10 @@ public abstract class IntegrationTest extends TestUtils {
|
|||||||
+ "\n " + Utils.listToString(mthNode.getAttributesStringsList(), "\n "));
|
+ "\n " + Utils.listToString(mthNode.getAttributesStringsList(), "\n "));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assertThat(cls.getCode().toString(), not(containsString("inconsistent")));
|
|
||||||
|
String code = cls.getCode().getCodeStr();
|
||||||
|
assertThat(code, not(containsString("inconsistent")));
|
||||||
|
assertThat(code, not(containsString("JADX ERROR")));
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasErrors(IAttributeNode node) {
|
private boolean hasErrors(IAttributeNode node) {
|
||||||
@@ -407,36 +433,6 @@ public abstract class IntegrationTest extends TestUtils {
|
|||||||
return dynamicCompiler.invoke(cls, methodName, types, args);
|
return dynamicCompiler.invoke(cls, methodName, types, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
private File getJarForClass(Class<?> cls) throws IOException {
|
|
||||||
List<File> files = compileClass(cls);
|
|
||||||
assertThat("File list is empty", files, not(empty()));
|
|
||||||
|
|
||||||
String path = cls.getPackage().getName().replace('.', '/');
|
|
||||||
File temp = createTempFile(".jar");
|
|
||||||
try (JarOutputStream jo = new JarOutputStream(new FileOutputStream(temp))) {
|
|
||||||
for (File file : files) {
|
|
||||||
addFileToJar(jo, file, path + '/' + file.getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected File createTempFile(String suffix) {
|
|
||||||
try {
|
|
||||||
Path temp;
|
|
||||||
if (deleteTmpFiles) {
|
|
||||||
temp = FileUtils.createTempFile(suffix);
|
|
||||||
} else {
|
|
||||||
// don't delete on exit
|
|
||||||
temp = FileUtils.createTempFileNoDelete(suffix);
|
|
||||||
System.out.println("Temporary file saved: " + temp.toAbsolutePath());
|
|
||||||
}
|
|
||||||
return temp.toFile();
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new AssertionError(e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private List<File> compileClass(Class<?> cls) throws IOException {
|
private List<File> compileClass(Class<?> cls) throws IOException {
|
||||||
String clsFullName = cls.getName();
|
String clsFullName = cls.getName();
|
||||||
String rootClsName;
|
String rootClsName;
|
||||||
@@ -499,10 +495,6 @@ public abstract class IntegrationTest extends TestUtils {
|
|||||||
this.compile = false;
|
this.compile = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void dontUnloadClass() {
|
|
||||||
this.unloadCls = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void enableDeobfuscation() {
|
protected void enableDeobfuscation() {
|
||||||
args.setDeobfuscationOn(true);
|
args.setDeobfuscationOn(true);
|
||||||
args.setDeobfuscationForceSave(true);
|
args.setDeobfuscationForceSave(true);
|
||||||
@@ -518,6 +510,22 @@ public abstract class IntegrationTest extends TestUtils {
|
|||||||
printLineNumbers = true;
|
printLineNumbers = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void printOffsets() {
|
||||||
|
printOffsets = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void useJavaInput() {
|
||||||
|
this.useJavaInput = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void useDexInput() {
|
||||||
|
this.useJavaInput = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isJavaInput() {
|
||||||
|
return Utils.getOrElse(useJavaInput, USE_JAVA_INPUT);
|
||||||
|
}
|
||||||
|
|
||||||
// Use only for debug purpose
|
// Use only for debug purpose
|
||||||
@Deprecated
|
@Deprecated
|
||||||
protected void outputCFG() {
|
protected void outputCFG() {
|
||||||
@@ -527,8 +535,8 @@ public abstract class IntegrationTest extends TestUtils {
|
|||||||
|
|
||||||
// Use only for debug purpose
|
// Use only for debug purpose
|
||||||
@Deprecated
|
@Deprecated
|
||||||
protected void printSmali() {
|
protected void printDisassemble() {
|
||||||
this.printSmali = true;
|
this.printDisassemble = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use only for debug purpose
|
// Use only for debug purpose
|
||||||
@@ -536,10 +544,4 @@ public abstract class IntegrationTest extends TestUtils {
|
|||||||
protected void outputRawCFG() {
|
protected void outputRawCFG() {
|
||||||
this.args.setRawCFGOutput(true);
|
this.args.setRawCFGOutput(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use only for debug purpose
|
|
||||||
@Deprecated
|
|
||||||
protected void notDeleteTmpJar() {
|
|
||||||
this.deleteTmpFiles = false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import java.util.stream.Collectors;
|
|||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
|
||||||
import jadx.api.JadxInternalAccess;
|
import jadx.api.JadxInternalAccess;
|
||||||
import jadx.core.dex.nodes.ClassNode;
|
import jadx.core.dex.nodes.ClassNode;
|
||||||
@@ -21,6 +22,12 @@ public abstract class SmaliTest extends IntegrationTest {
|
|||||||
private static final String SMALI_TESTS_DIR = "src/test/smali";
|
private static final String SMALI_TESTS_DIR = "src/test/smali";
|
||||||
private static final String SMALI_TESTS_EXT = ".smali";
|
private static final String SMALI_TESTS_EXT = ".smali";
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void init() {
|
||||||
|
super.init();
|
||||||
|
this.useDexInput();
|
||||||
|
}
|
||||||
|
|
||||||
protected ClassNode getClassNodeFromSmali(String file, String clsName) {
|
protected ClassNode getClassNodeFromSmali(String file, String clsName) {
|
||||||
File smaliFile = getSmaliFile(file);
|
File smaliFile = getSmaliFile(file);
|
||||||
return getClassNodeFromFiles(Collections.singletonList(smaliFile), clsName);
|
return getClassNodeFromFiles(Collections.singletonList(smaliFile), clsName);
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package jadx.tests.api.utils.assertj;
|
package jadx.tests.api.utils.assertj;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
import org.assertj.core.api.AbstractStringAssert;
|
import org.assertj.core.api.AbstractStringAssert;
|
||||||
|
|
||||||
import jadx.api.ICodeWriter;
|
import jadx.api.ICodeWriter;
|
||||||
@@ -72,4 +74,15 @@ public class JadxCodeAssertions extends AbstractStringAssert<JadxCodeAssertions>
|
|||||||
System.out.println("-----------------------------------------------------------");
|
System.out.println("-----------------------------------------------------------");
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public JadxCodeAssertions containsOneOf(String... substringArr) {
|
||||||
|
int matches = 0;
|
||||||
|
for (String substring : substringArr) {
|
||||||
|
matches += TestUtils.count(actual, substring);
|
||||||
|
}
|
||||||
|
if (matches != 1) {
|
||||||
|
failWithMessage("Expected a only one match from <%s> but was <%d>", Arrays.toString(substringArr), matches);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ package jadx.tests.functional;
|
|||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import jadx.api.plugins.input.data.attributes.IJadxAttribute;
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.core.dex.attributes.AType;
|
||||||
import jadx.core.dex.attributes.AttributeStorage;
|
import jadx.core.dex.attributes.AttributeStorage;
|
||||||
import jadx.core.dex.attributes.IAttribute;
|
|
||||||
|
|
||||||
import static jadx.core.dex.attributes.AFlag.SYNTHETIC;
|
import static jadx.core.dex.attributes.AFlag.SYNTHETIC;
|
||||||
import static org.hamcrest.MatcherAssert.assertThat;
|
import static org.hamcrest.MatcherAssert.assertThat;
|
||||||
@@ -35,9 +35,9 @@ public class AttributeStorageTest {
|
|||||||
|
|
||||||
public static final AType<TestAttr> TEST = new AType<>();
|
public static final AType<TestAttr> TEST = new AType<>();
|
||||||
|
|
||||||
public static class TestAttr implements IAttribute {
|
public static class TestAttr implements IJadxAttribute {
|
||||||
@Override
|
@Override
|
||||||
public AType<TestAttr> getType() {
|
public AType<TestAttr> getAttrType() {
|
||||||
return TEST;
|
return TEST;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ import java.util.Map;
|
|||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import jadx.core.dex.attributes.AType;
|
import jadx.api.plugins.input.data.annotations.EncodedValue;
|
||||||
import jadx.core.dex.attributes.fldinit.FieldInitAttr;
|
import jadx.api.plugins.input.data.attributes.JadxAttrType;
|
||||||
import jadx.core.dex.nodes.ClassNode;
|
import jadx.core.dex.nodes.ClassNode;
|
||||||
import jadx.core.dex.nodes.FieldNode;
|
import jadx.core.dex.nodes.FieldNode;
|
||||||
import jadx.tests.api.IntegrationTest;
|
import jadx.tests.api.IntegrationTest;
|
||||||
@@ -56,8 +56,8 @@ public class TestRFieldRestore extends IntegrationTest {
|
|||||||
// check 'Button' field
|
// check 'Button' field
|
||||||
FieldNode buttonField = idCls.searchFieldByName("Button");
|
FieldNode buttonField = idCls.searchFieldByName("Button");
|
||||||
assertThat(buttonField, notNullValue());
|
assertThat(buttonField, notNullValue());
|
||||||
FieldInitAttr fieldInitAttr = buttonField.get(AType.FIELD_INIT);
|
EncodedValue constVal = buttonField.get(JadxAttrType.CONSTANT_VALUE);
|
||||||
Integer buttonValue = (Integer) fieldInitAttr.getEncodedValue().getValue();
|
Integer buttonValue = (Integer) constVal.getValue();
|
||||||
assertThat(buttonValue, is(buttonConstValue));
|
assertThat(buttonValue, is(buttonConstValue));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ public class TestAnnotationsMix extends IntegrationTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void test() {
|
public void test() {
|
||||||
|
// useDexInput();
|
||||||
ClassNode cls = getClassNode(TestCls.class);
|
ClassNode cls = getClassNode(TestCls.class);
|
||||||
String code = cls.getCode().toString();
|
String code = cls.getCode().toString();
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user