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