package jadx.core.dex.nodes;
import jadx.core.dex.info.ClassInfo;
import jadx.core.dex.info.FieldInfo;
import jadx.core.dex.info.InfoStorage;
import jadx.core.dex.info.MethodInfo;
import jadx.core.dex.instructions.args.ArgType;
import jadx.core.utils.exceptions.DecodeException;
import jadx.core.utils.files.InputFile;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.android.dex.ClassData;
import com.android.dex.ClassData.Method;
import com.android.dex.ClassDef;
import com.android.dex.Code;
import com.android.dex.Dex;
import com.android.dex.Dex.Section;
import com.android.dex.FieldId;
import com.android.dex.MethodId;
import com.android.dex.ProtoId;
import com.android.dex.TypeList;
public class DexNode {
public static final int NO_INDEX = -1;
private final RootNode root;
private final Dex dexBuf;
private final InputFile file;
private final List<ClassNode> classes = new ArrayList<ClassNode>();
private final Map<ClassInfo, ClassNode> clsMap = new HashMap<ClassInfo, ClassNode>();
private final Map<Object, FieldNode> constFields = new HashMap<Object, FieldNode>();
private final InfoStorage infoStorage = new InfoStorage();
public DexNode(RootNode root, InputFile input) {
this.root = root;
this.file = input;
this.dexBuf = input.getDexBuffer();
}
public void loadClasses() throws DecodeException {
for (ClassDef cls : dexBuf.classDefs()) {
ClassNode clsNode = new ClassNode(this, cls);
classes.add(clsNode);
clsMap.put(clsNode.getClassInfo(), clsNode);
}
}
void initInnerClasses() {
// move inner classes
List<ClassNode> inner = new ArrayList<ClassNode>();
for (ClassNode cls : classes) {
if (cls.getClassInfo().isInner()) {
inner.add(cls);
}
}
for (ClassNode cls : inner) {
ClassInfo clsInfo = cls.getClassInfo();
ClassNode parent = resolveClass(clsInfo.getParentClass());
if (parent == null) {
clsMap.remove(clsInfo);
clsInfo.notInner(cls.dex());
clsMap.put(clsInfo, cls);
} else {
parent.addInnerClass(cls);
}
}
}
public List<ClassNode> getClasses() {
return classes;
}
@Nullable
public ClassNode resolveClass(ClassInfo clsInfo) {
return clsMap.get(clsInfo);
}
@Nullable
public ClassNode resolveClass(@NotNull ArgType type) {
if (type.isGeneric()) {
type = ArgType.object(type.getObject());
}
return resolveClass(ClassInfo.fromType(this, type));
}
@Nullable
public MethodNode resolveMethod(@NotNull MethodInfo mth) {
ClassNode cls = resolveClass(mth.getDeclClass());
if (cls != null) {
return cls.searchMethod(mth);
}
return null;
}
/**
* Search method in class hierarchy.
*/
@Nullable
public MethodNode deepResolveMethod(@NotNull MethodInfo mth) {
ClassNode cls = resolveClass(mth.getDeclClass());
if (cls == null) {
return null;
}
return deepResolveMethod(cls, mth.makeSignature(false));
}
@Nullable
private MethodNode deepResolveMethod(@NotNull ClassNode cls, String signature) {
for (MethodNode m : cls.getMethods()) {
if (m.getMethodInfo().getShortId().startsWith(signature)) {
return m;
}
}
MethodNode found;
ArgType superClass = cls.getSuperClass();
if (superClass != null) {
ClassNode superNode = resolveClass(superClass);
if (superNode != null) {
found = deepResolveMethod(superNode, signature);
if (found != null) {
return found;
}
}
}
for (ArgType iFaceType : cls.getInterfaces()) {
ClassNode iFaceNode = resolveClass(iFaceType);
if (iFaceNode != null) {
found = deepResolveMethod(iFaceNode, signature);
if (found != null) {
return found;
}
}
}
return null;
}
@Nullable
public FieldNode resolveField(FieldInfo field) {
ClassNode cls = resolveClass(field.getDeclClass());
if (cls != null) {
return cls.searchField(field);
}
return null;
}
public Map<Object, FieldNode> getConstFields() {
return constFields;
}
public InfoStorage getInfoStorage() {
return infoStorage;
}
public InputFile getInputFile() {
return file;
}
// DexBuffer wrappers
public String getString(int index) {
return dexBuf.strings().get(index);
}
public ArgType getType(int index) {
return ArgType.parse(getString(dexBuf.typeIds().get(index)));
}
public MethodId getMethodId(int mthIndex) {
return dexBuf.methodIds().get(mthIndex);
}
public FieldId getFieldId(int fieldIndex) {
return dexBuf.fieldIds().get(fieldIndex);
}
public ProtoId getProtoId(int protoIndex) {
return dexBuf.protoIds().get(protoIndex);
}
public ClassData readClassData(ClassDef cls) {
return dexBuf.readClassData(cls);
}
public List<ArgType> readParamList(int parametersOffset) {
TypeList paramList = dexBuf.readTypeList(parametersOffset);
List<ArgType> args = new ArrayList<ArgType>(paramList.getTypes().length);
for (short t : paramList.getTypes()) {
args.add(getType(t));
}
return Collections.unmodifiableList(args);
}
public Code readCode(Method mth) {
return dexBuf.readCode(mth);
}
public Section openSection(int offset) {
return dexBuf.open(offset);
}
public RootNode root() {
return root;
}
@Override
public String toString() {
return "DEX";
}
}