Package jadx.core.dex.nodes

Source Code of jadx.core.dex.nodes.ClassNode

package jadx.core.dex.nodes;

import jadx.core.Consts;
import jadx.core.codegen.CodeWriter;
import jadx.core.dex.attributes.AType;
import jadx.core.dex.attributes.annotations.Annotation;
import jadx.core.dex.attributes.nodes.JadxErrorAttr;
import jadx.core.dex.attributes.nodes.LineAttrNode;
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;
import jadx.core.dex.info.FieldInfo;
import jadx.core.dex.info.MethodInfo;
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.nodes.parser.AnnotationsParser;
import jadx.core.dex.nodes.parser.FieldValueAttr;
import jadx.core.dex.nodes.parser.SignatureParser;
import jadx.core.dex.nodes.parser.StaticValuesParser;
import jadx.core.utils.exceptions.DecodeException;
import jadx.core.utils.exceptions.JadxRuntimeException;

import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.android.dex.ClassData;
import com.android.dex.ClassData.Field;
import com.android.dex.ClassData.Method;
import com.android.dex.ClassDef;

public class ClassNode extends LineAttrNode implements ILoadable {
  private static final Logger LOG = LoggerFactory.getLogger(ClassNode.class);

  private final DexNode dex;
  private final ClassInfo clsInfo;
  private final AccessInfo accessFlags;
  private ClassInfo superClass;
  private List<ClassInfo> interfaces;
  private Map<ArgType, List<ArgType>> genericMap;

  private final List<MethodNode> methods;
  private final List<FieldNode> fields;
  private Map<Object, FieldNode> constFields = Collections.emptyMap();
  private List<ClassNode> innerClasses = Collections.emptyList();

  // store decompiled code
  private CodeWriter code;
  // store parent for inner classes or 'this' otherwise
  private ClassNode parentClass;

  public ClassNode(DexNode dex, ClassDef cls) throws DecodeException {
    this.dex = dex;
    this.clsInfo = ClassInfo.fromDex(dex, cls.getTypeIndex());
    try {
      if (cls.getSupertypeIndex() == DexNode.NO_INDEX) {
        this.superClass = null;
      } else {
        this.superClass = ClassInfo.fromDex(dex, cls.getSupertypeIndex());
      }
      this.interfaces = new ArrayList<ClassInfo>(cls.getInterfaces().length);
      for (short interfaceIdx : cls.getInterfaces()) {
        this.interfaces.add(ClassInfo.fromDex(dex, interfaceIdx));
      }
      if (cls.getClassDataOffset() != 0) {
        ClassData clsData = dex.readClassData(cls);
        int mthsCount = clsData.getDirectMethods().length + clsData.getVirtualMethods().length;
        int fieldsCount = clsData.getStaticFields().length + clsData.getInstanceFields().length;

        methods = new ArrayList<MethodNode>(mthsCount);
        fields = new ArrayList<FieldNode>(fieldsCount);

        for (Method mth : clsData.getDirectMethods()) {
          methods.add(new MethodNode(this, mth));
        }
        for (Method mth : clsData.getVirtualMethods()) {
          methods.add(new MethodNode(this, mth));
        }

        for (Field f : clsData.getStaticFields()) {
          fields.add(new FieldNode(this, f));
        }
        loadStaticValues(cls, fields);
        for (Field f : clsData.getInstanceFields()) {
          fields.add(new FieldNode(this, f));
        }
      } else {
        methods = Collections.emptyList();
        fields = Collections.emptyList();
      }

      loadAnnotations(cls);

      parseClassSignature();
      setFieldsTypesFromSignature();

      int sfIdx = cls.getSourceFileIndex();
      if (sfIdx != DexNode.NO_INDEX) {
        String fileName = dex.getString(sfIdx);
        if (!this.getFullName().contains(fileName.replace(".java", ""))
            && !fileName.equals("SourceFile")) {
          this.addAttr(new SourceFileAttr(fileName));
          LOG.debug("Class '{}' compiled from '{}'", this, fileName);
        }
      }

      // restore original access flags from dalvik annotation if present
      int accFlagsValue;
      Annotation a = getAnnotation(Consts.DALVIK_INNER_CLASS);
      if (a != null) {
        accFlagsValue = (Integer) a.getValues().get("accessFlags");
      } else {
        accFlagsValue = cls.getAccessFlags();
      }
      this.accessFlags = new AccessInfo(accFlagsValue, AFType.CLASS);

    } catch (Exception e) {
      throw new DecodeException("Error decode class: " + getFullName(), e);
    }
  }

  private void loadAnnotations(ClassDef cls) {
    int offset = cls.getAnnotationsOffset();
    if (offset != 0) {
      try {
        new AnnotationsParser(this).parse(offset);
      } catch (DecodeException e) {
        LOG.error("Error parsing annotations in {}", this, e);
      }
    }
  }

  private void loadStaticValues(ClassDef cls, List<FieldNode> staticFields) throws DecodeException {
    for (FieldNode f : staticFields) {
      if (f.getAccessFlags().isFinal()) {
        f.addAttr(new FieldValueAttr(null));
      }
    }

    int offset = cls.getStaticValuesOffset();
    if (offset != 0) {
      StaticValuesParser parser = new StaticValuesParser(dex, dex.openSection(offset));
      int count = parser.processFields(staticFields);
      constFields = new LinkedHashMap<Object, FieldNode>(count);
      for (FieldNode f : staticFields) {
        AccessInfo accFlags = f.getAccessFlags();
        if (accFlags.isStatic() && accFlags.isFinal()) {
          FieldValueAttr fv = f.get(AType.FIELD_VALUE);
          if (fv != null && fv.getValue() != null) {
            if (accFlags.isPublic()) {
              dex.getConstFields().put(fv.getValue(), f);
            }
            constFields.put(fv.getValue(), f);
          }
        }
      }
    }
  }

  private void parseClassSignature() {
    SignatureParser sp = SignatureParser.fromNode(this);
    if (sp == null) {
      return;
    }
    try {
      // parse class generic map
      genericMap = sp.consumeGenericMap();
      // parse super class signature
      superClass = ClassInfo.fromType(sp.consumeType());
      // parse interfaces signatures
      for (int i = 0; i < interfaces.size(); i++) {
        ArgType type = sp.consumeType();
        if (type != null) {
          interfaces.set(i, ClassInfo.fromType(type));
        } else {
          break;
        }
      }
    } catch (JadxRuntimeException e) {
      LOG.error("Class signature parse error: {}", this, e);
    }
  }

  private void setFieldsTypesFromSignature() {
    for (FieldNode field : fields) {
      SignatureParser sp = SignatureParser.fromNode(field);
      if (sp != null) {
        try {
          ArgType gType = sp.consumeType();
          if (gType != null) {
            field.setType(gType);
          }
        } catch (JadxRuntimeException e) {
          LOG.error("Field signature parse error: {}", field, e);
        }
      }
    }
  }

  @Override
  public void load() {
    for (MethodNode mth : getMethods()) {
      try {
        mth.load();
      } catch (DecodeException e) {
        LOG.error("Method load error", e);
        mth.addAttr(new JadxErrorAttr(e));
      }
    }
    for (ClassNode innerCls : getInnerClasses()) {
      innerCls.load();
    }
  }

  @Override
  public void unload() {
    for (MethodNode mth : getMethods()) {
      mth.unload();
    }
    for (ClassNode innerCls : getInnerClasses()) {
      innerCls.unload();
    }
  }

  public ClassInfo getSuperClass() {
    return superClass;
  }

  public List<ClassInfo> getInterfaces() {
    return interfaces;
  }

  public Map<ArgType, List<ArgType>> getGenericMap() {
    return genericMap;
  }

  public List<MethodNode> getMethods() {
    return methods;
  }

  public List<FieldNode> getFields() {
    return fields;
  }

  public FieldNode getConstField(Object obj) {
    return getConstField(obj, true);
  }

  public FieldNode getConstField(Object obj, boolean searchGlobal) {
    ClassNode cn = this;
    FieldNode field;
    do {
      field = cn.constFields.get(obj);
    }
    while (field == null
        && (cn.clsInfo.getParentClass() != null)
        && (cn = dex.resolveClass(cn.clsInfo.getParentClass())) != null);

    if (field == null && searchGlobal) {
      field = dex.getConstFields().get(obj);
    }
    return field;
  }

  public FieldNode getConstFieldByLiteralArg(LiteralArg arg) {
    PrimitiveType type = arg.getType().getPrimitiveType();
    if (type == null) {
      return null;
    }
    long literal = arg.getLiteral();
    switch (type) {
      case BOOLEAN:
        return getConstField(literal == 1, false);
      case CHAR:
        return getConstField((char) literal, Math.abs(literal) > 1);
      case BYTE:
        return getConstField((byte) literal, Math.abs(literal) > 1);
      case SHORT:
        return getConstField((short) literal, Math.abs(literal) > 1);
      case INT:
        return getConstField((int) literal, Math.abs(literal) > 1);
      case LONG:
        return getConstField(literal, Math.abs(literal) > 1);
      case FLOAT:
        return getConstField(Float.intBitsToFloat((int) literal), true);
      case DOUBLE:
        return getConstField(Double.longBitsToDouble(literal), true);
    }
    return null;
  }

  public FieldNode searchFieldById(int id) {
    String name = FieldInfo.getNameById(dex, id);
    for (FieldNode f : fields) {
      if (f.getName().equals(name)) {
        return f;
      }
    }
    return null;
  }

  public FieldNode searchField(FieldInfo field) {
    return searchFieldByName(field.getName());
  }

  public FieldNode searchFieldByName(String name) {
    for (FieldNode f : fields) {
      if (f.getName().equals(name)) {
        return f;
      }
    }
    return null;
  }

  public MethodNode searchMethod(MethodInfo mth) {
    for (MethodNode m : methods) {
      if (m.getMethodInfo().equals(mth)) {
        return m;
      }
    }
    return null;
  }

  public MethodNode searchMethodByName(String shortId) {
    for (MethodNode m : methods) {
      if (m.getMethodInfo().getShortId().equals(shortId)) {
        return m;
      }
    }
    return null;
  }

  public MethodNode searchMethodById(int id) {
    return searchMethodByName(MethodInfo.fromDex(dex, id).getShortId());
  }

  public ClassNode getParentClass() {
    if (parentClass == null) {
      if (clsInfo.isInner()) {
        ClassNode parent = dex().resolveClass(clsInfo.getParentClass());
        parent = parent == null ? this : parent;
        parentClass = parent;
      } else {
        parentClass = this;
      }
    }
    return parentClass;
  }

  public ClassNode getTopParentClass() {
    ClassNode parent = getParentClass();
    return parent == this ? this : parent.getParentClass();
  }

  public List<ClassNode> getInnerClasses() {
    return innerClasses;
  }

  public void addInnerClass(ClassNode cls) {
    if (innerClasses.isEmpty()) {
      innerClasses = new ArrayList<ClassNode>(3);
    }
    innerClasses.add(cls);
  }

  public boolean isEnum() {
    return getAccessFlags().isEnum() && getSuperClass().getFullName().equals(Consts.CLASS_ENUM);
  }

  public boolean isAnonymous() {
    return clsInfo.isInner()
        && getShortName().startsWith(Consts.ANONYMOUS_CLASS_PREFIX)
        && getDefaultConstructor() != null;
  }

  public MethodNode getDefaultConstructor() {
    for (MethodNode mth : methods) {
      if (mth.isDefaultConstructor()) {
        return mth;
      }
    }
    return null;
  }

  public AccessInfo getAccessFlags() {
    return accessFlags;
  }

  public DexNode dex() {
    return dex;
  }

  public ClassInfo getClassInfo() {
    return clsInfo;
  }

  public String getShortName() {
    return clsInfo.getShortName();
  }

  public String getFullName() {
    return clsInfo.getFullName();
  }

  public String getPackage() {
    return clsInfo.getPackage();
  }

  public String getRawName() {
    return clsInfo.getRawName();
  }

  public void setCode(CodeWriter code) {
    this.code = code;
  }

  public CodeWriter getCode() {
    return code;
  }

  @Override
  public String toString() {
    return getFullName();
  }
}
TOP

Related Classes of jadx.core.dex.nodes.ClassNode

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.