/* * 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 jerl.bcm.util; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.util.Vector; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import org.objectweb.asm.ClassAdapter; import org.objectweb.asm.ClassReader; import org.objectweb.asm.ClassVisitor; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Opcodes; public class ClassInfoLoader implements CommonSuperClassIF { private final Vector<ZipFile> archiveFiles = new Vector<ZipFile>(); private boolean isDebugMode = true; public ClassInfoLoader(ZipFile file) { archiveFiles.add(file); } /** * @param mode */ public void setDebugMode(boolean mode) { isDebugMode = mode; } public void addArchive(ZipFile file) { archiveFiles.add(file); } private String convertToFilename(String className) { // className.replace('.', newChar) return className + ".class"; } private InputStream getInputStream(String className) throws IOException { String filename = convertToFilename(className); for (int i = 0; i < archiveFiles.size(); i++) { ZipFile zipFile = archiveFiles.elementAt(i); ZipEntry entry = zipFile.getEntry(filename); if (entry != null) { return zipFile.getInputStream(entry); } } return null; } // TODO: make a cache of ClassInfo instances private ClassInfo loadClassInfo(String name) throws ClassNotFoundException { ClassReader cr = null; try { InputStream is = getInputStream(name); if (is != null) { cr = new ClassReader(is); } else { throw new ClassNotFoundException(name); } } catch (IOException ioe) { throw new ClassNotFoundException(name); } ClassWriter cw = new ClassWriter(0); ClassInfoBuilder cv = new ClassInfoBuilder(cw); cr.accept(cv, 0); return cv.getClassInfo(); } @Override public String getCommonSuperClass(final String type1, final String type2) { if (isDebugMode) { System.out.print("findCommonSuperClass('" + type1 + "', '" + type2 + "')"); } ClassInfo ci1 = null; ClassInfo ci2 = null; try { ci1 = loadClassInfo(type1); } catch (ClassNotFoundException cnfe) { if (isDebugMode) { System.out.println("ERROR: " + cnfe); } } try { ci2 = loadClassInfo(type2); } catch (ClassNotFoundException cnfe) { if (isDebugMode) { System.out.println("ERROR: " + cnfe); } } if (ci1.isAssignableFrom(ci2)) { if (isDebugMode) { System.out.println(" : " + type1); } return type1; } if (ci2.isAssignableFrom(ci1)) { if (isDebugMode) { System.out.println(" : " + type2); } return type2; } if (ci1.isInterface || ci2.isInterface) { if (isDebugMode) { System.out.println(" : java/lang/Object"); } return "java/lang/Object"; } else { // step up in class hierachy to find common super class do { ci1 = ci1.getSuperClassInfo(); } while (!ci1.isAssignableFrom(ci2)); if (isDebugMode) { System.out.println(" : " + ci1.name); } return ci1.name; } } public static void main(String[] args) throws IOException { File libFile = new File("res/javaclasses.zip"); File jarFile = new File("res/MMP-SE_K790.jar"); ClassInfoLoader cr = new ClassInfoLoader(new ZipFile(jarFile)); cr.setDebugMode(true); cr.addArchive(new ZipFile(libFile)); //cr.findCommonSuperClass("a2", "a7"); System.out.println(cr.getCommonSuperClass("java/lang/Object", "a7")); System.out.println(cr.getCommonSuperClass("oh", "dr")); } /////// ClassInfoBuilder //////////////////////////////////////////////////////// class ClassInfoBuilder extends ClassAdapter { private String superName; private String name; private String[] interfaces; private boolean isInterface; public ClassInfoBuilder(ClassVisitor cv) { super(cv); } @Override public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { this.name = name; this.superName = superName; this.interfaces = interfaces; isInterface = (access & Opcodes.ACC_INTERFACE) == 0 ? false : true; cv.visit(version, access, name, signature, superName, interfaces); } public ClassInfo getClassInfo() { return new ClassInfo(name, superName, interfaces, isInterface); } } /////// ClassInfo //////////////////////////////////////////////////////////// class ClassInfo { public final String superName; public final String name; private final String[] interfaces; private final boolean isInterface; public ClassInfo(String name, String superName, String[] interfaces, boolean isInterface) { this.name = name; this.superName = superName; this.interfaces = interfaces; this.isInterface = isInterface; } @Override public String toString() { return isInterface ? "interface " : "class " + name + " extends " + superName + " implements " + interfaces.length + " interfaces"; } public ClassInfo getSuperClassInfo() { try { return loadClassInfo(superName); } catch (ClassNotFoundException cnfe) { return null; } } /** * Determines if the class or interface represented by this <code>Class</code> * object is either the same as, or is a superclass or superinterface of, * the class or interface represented by the specified <code>Class</code> * parameter. It returns <code>true</code> if so; otherwise it returns * <code>false</code>. */ public boolean isAssignableFrom(ClassInfo ci) { if (name.equals(ci.name) || name.equals("java/lang/Object")) { // same class or this is java.lang.Object in which case ci is assignable return true; } // is this a super class of ci while (!ci.name.equals("java/lang/Object")) { ci = ci.getSuperClassInfo(); if (name.equals(ci.name)) { // same class return true; } } return false; } } }