/* * Copyright (C) 2012 Sony Mobile Communications AB * * This file is part of ApkAnalyser. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package mereflect; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.TreeSet; import mereflect.info.AttributeInfo; import mereflect.info.CiClass; import mereflect.info.CiUtf8; import mereflect.info.ClassInfo; public class MEClass implements Type, Comparable<Object> { public static final int ACC_PUBLIC = 0x0001; public static final int ACC_FINAL = 0x0010; public static final int ACC_SUPER = 0x0020; public static final int ACC_INTERFACE = 0x0200; public static final int ACC_ABSTRACT = 0x0400; protected int m_majorVersion; protected int m_minorVersion; protected ClassInfo[] m_constantPool; protected int m_accessFlags; protected int m_thisClassIndex; protected int m_superClassIndex; protected int[] m_interfaceIndices; protected MEMethod[] m_methods; protected MEField[] m_fields; protected AttributeInfo[] m_attributes; protected MEClassResource m_resource; protected MEClass m_superClass = null; protected MEClass[] m_interfaces = null; // Logic /** * Returns the class name as appearing in constant pool */ public String getRawName() { CiClass classInfo = (CiClass) getConstantPool()[m_thisClassIndex]; return ((CiUtf8) getConstantPool()[classInfo.getNameIndex()]).getUtf8(); } /** * Returns full name of class * @return class name including package */ @Override public String getName() { return getRawName().replace('/', '.'); } /** * Returns class name, excluding package declaration * @return class name excluding package */ public String getClassName() { String clName = getName(); int dIndex = clName.lastIndexOf('.'); if (dIndex >= 0) { clName = clName.substring(dIndex + 1); } return clName; } /** * Returns superclass * @return the superclass of this type */ public MEClass getSuperClass() { if (m_superClass == null) { if (m_superClassIndex == 0) { return null; } CiClass classInfo = (CiClass) getConstantPool()[m_superClassIndex]; CiUtf8 classDef = (CiUtf8) getConstantPool()[classInfo.getNameIndex()]; String classname = classDef.getUtf8().replace('/', '.'); try { m_superClass = getResource().getContext().getMEClass(classname); } catch (IOException e) { throw new RuntimeException(e); } catch (ClassNotFoundException e) { m_superClass = new UnknownClass(classname, getResource()); } } return m_superClass; } public MEClass[] getInterfaces() { if (m_interfaces == null) { m_interfaces = new MEClass[m_interfaceIndices.length]; for (int i = 0; i < m_interfaces.length; i++) { int ifcIdx = m_interfaceIndices[i]; CiClass ifcInfo = (CiClass) getConstantPool()[ifcIdx]; CiUtf8 ifcDef = (CiUtf8) getConstantPool()[ifcInfo.getNameIndex()]; String ifcName = ifcDef.getUtf8().replace('/', '.'); try { m_interfaces[i] = getResource().getContext().getMEClass(ifcName); } catch (IOException e) { throw new RuntimeException(e); } catch (ClassNotFoundException e) { m_interfaces[i] = new UnknownClass(ifcName, getResource()); } } } return m_interfaces; } /** * Returns true if this class has public access * @return if this class has public access */ public boolean isPublic() { return (m_accessFlags & ACC_PUBLIC) > 0; } /** * Returns true if this class is declared final * @return if this class is final */ public boolean isFinal() { return (m_accessFlags & ACC_FINAL) > 0; } /** * Returns true if this class is super - excerpt from VM Spec:<br> * Treat superclass methods specially when invoked by the invokespecial instruction * @return if this class is super */ public boolean isSuper() { return (m_accessFlags & ACC_SUPER) > 0; } /** * Returns true if this class is an interface * @return if this class is an interface */ public boolean isInterface() { return (m_accessFlags & ACC_INTERFACE) > 0; } public int getFlags() { return m_accessFlags; } /** * Returns true if this class is declared abstract * @return if this class is abstract */ public boolean isAbstract() { return (m_accessFlags & ACC_ABSTRACT) > 0; } @Override public boolean isPrimitive() { return false; } @Override public boolean isArray() { return false; } /** * Returns all class references found in constantpool * @return String array of all class references */ public String[] getDependencies() { TreeSet<String> refClasses = new TreeSet<String>(); for (int i = 1; i < m_constantPool.length; i++) { ClassInfo ci = m_constantPool[i]; if (ci != null && ci.getTag() == ClassInfo.CONSTANT_Class) { int classNameIndex = ((CiClass) ci).getNameIndex(); if (classNameIndex > 0 && classNameIndex < m_constantPool.length && m_constantPool[classNameIndex] != null && m_constantPool[classNameIndex].getTag() == ClassInfo.CONSTANT_Utf8) { String cname = ((CiUtf8) m_constantPool[classNameIndex]).getUtf8(); int arrayIx = cname.lastIndexOf('['); int clEndIx = cname.indexOf(';'); if (arrayIx < 0) { refClasses.add(cname); } else if (arrayIx >= 0 && clEndIx > 0) { cname = cname.substring(arrayIx + 2, cname.length() - 1); // e.g. [[Ljava/lang/Object; refClasses.add(cname); } } } } return refClasses.toArray(new String[refClasses.size()]); } /** * Returns all methods whose methodname matches paraemter. * Only searches this class, not in superclasses nor implemented interfaces. * @param methodName the matching methodname to search for * @return array of methods, or empty array of no matching methods were found */ public MEMethod[] getMethods(String methodName) { List<MEMethod> res = new ArrayList<MEMethod>(); MEMethod[] meths = getMethods(); for (int i = 0; i < meths.length; i++) { if (meths[i].getName().equals(methodName)) { res.add(meths[i]); } } return res.toArray(new MEMethod[res.size()]); } public String getUnknownSuperClassName() { MEClass clazz = this; String className = null; while (clazz != null && (!(clazz instanceof UnknownClass))) { clazz = clazz.getSuperClass(); } if (clazz != null && clazz instanceof UnknownClass) { className = clazz.getName(); } return className; } public MEField getField(String fieldName, String descriptor) { MEField res = null; MEField[] fields = getFields(); if (fields != null) { for (int i = 0; i < fields.length; i++) { if (fields[i].getName().equals(fieldName) && (descriptor == null || fields[i].getDescriptor().equals(descriptor))) { res = fields[i]; break; } } } if (res == null) { MEClass superClass = getSuperClass(); if (superClass != null && !(superClass instanceof UnknownClass)) { res = superClass.getField(fieldName, descriptor); } } return res; } /** * Returns method matching methodname and descriptor. The format * of the descriptor is according to the constant pool definition. * This method also searches superclasses and implemented interfaces. * @param methodName matching name of method * @param descriptor matching descriptor, or null for wildcard. * If descriptor is null, arbitrary matching method * will be returned. * @return Matching method or null */ public MEMethod getMethod(String methodName, String descriptor) { MEMethod res = null; MEMethod[] meths = getMethods(); if (meths != null) { for (int i = 0; i < meths.length; i++) { if (meths[i].getName().equals(methodName) && (descriptor == null || meths[i].getDescriptor().equals(descriptor))) { res = meths[i]; break; } } } if (res == null) { MEClass superClass = getSuperClass(); if (superClass != null && !(superClass instanceof UnknownClass)) { res = superClass.getMethod(methodName, descriptor); } } if (res == null) { MEClass[] ifcs = getInterfaces(); for (int i = 0; i < ifcs.length && res == null; i++) { if (!(ifcs[i] instanceof UnknownClass)) { res = ifcs[i].getMethod(methodName, descriptor); } } } return res; } /** * Returns method matching methodname and descriptor. The format * of the descriptor is according to the constant pool definition. * This method does not search superclasses and implemented interfaces. * @param methodName matching name of method * @param descriptor matching descriptor, or null for wildcard. * If descriptor is null, no method will be returned. * @return Matching method or null */ public MEMethod getMethodIsolated(String methodName, String descriptor) { MEMethod res = null; MEMethod[] meths = getMethods(); for (int i = 0; i < meths.length; i++) { if (meths[i].getName().equals(methodName) && meths[i].getDescriptor().equals(descriptor)) { res = meths[i]; break; } } return res; } /** * Recursively goes through superclasses and interfaces of this * class to find a match against specified class. * @param p The class to search for * @return True if found in this or supertree, false if not. */ public boolean isInstanceOf(MEClass p) { if (equals(p)) { return true; } boolean res = false; MEClass superClass = getSuperClass(); if (superClass != null && !(superClass instanceof UnknownClass)) { res = superClass.isInstanceOf(p); } if (!res) { MEClass[] ifcs = getInterfaces(); for (int i = 0; ifcs != null && !res && i < ifcs.length; i++) { res = ifcs[i].isInstanceOf(p); } } return res; } // Setters public void setAccessFlags(int accessFlags) { m_accessFlags = accessFlags; } public void setAttributes(AttributeInfo[] attributes) { m_attributes = attributes; } public void setConstantPool(ClassInfo[] constantPool) { m_constantPool = constantPool; } public void setFields(MEField[] fields) { m_fields = fields; } public void setInterfaceIndices(int[] interfaces) { m_interfaceIndices = interfaces; } public void setMajorVersion(int majorVersion) { m_majorVersion = majorVersion; } public void setMethods(MEMethod[] methods) { m_methods = methods; } public void setMinorVersion(int minorVersion) { m_minorVersion = minorVersion; } public void setSuperClassIndex(int superClass) { m_superClassIndex = superClass; } public void setThisClassIndex(int thisClass) { m_thisClassIndex = thisClass; } public void setResource(MEClassResource rsc) { m_resource = rsc; } // Getters public int getAccessFlags() { return m_accessFlags; } public AttributeInfo[] getAttributes() { return m_attributes; } public ClassInfo[] getConstantPool() { return m_constantPool; } public MEField[] getFields() { return m_fields; } public int[] getInterfaceIndices() { return m_interfaceIndices; } public int getMajorVersion() { return m_majorVersion; } /** * Returns all methods in this class/interface. * Does not return methods of superclass nor implemented interfaces. * @return Array of methods. */ public MEMethod[] getMethods() { return m_methods; } public int getMinorVersion() { return m_minorVersion; } public int getSuperClassIndex() { return m_superClassIndex; } public int getThisClassIndex() { return m_thisClassIndex; } public MEClassResource getResource() { return m_resource; } @Override public int hashCode() { int hashCode = getName().hashCode(); hashCode = 31 * hashCode + getResource().getContextualSpecification().hashCode(); return hashCode; } @Override public boolean equals(Object o) { return (o != null && o instanceof MEClass && !(o instanceof UnknownClass) && ((MEClass) o).getResource().getContextualSpecification().equals(getResource().getContextualSpecification()) && ((MEClass) o).getName().equals(getName())); } @Override public String toString() { return getName(); } @Override public int compareTo(Object o) { if (o instanceof MEClass) { return getName().compareTo(((MEClass) o).getName()); } else { return getName().compareTo(o.toString()); } } }