/* * This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html. * * This file is a derivative of code released under the terms listed below. * */ /* * * Copyright (c) 2009-2012, * * Jonathan Bardin <astrosus@gmail.com> * Steve Suh <suhsteve@gmail.com> * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. The names of the contributors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. * * */ package com.ibm.wala.dalvik.classLoader; import static com.ibm.wala.classLoader.ClassLoaderImpl.DEBUG_LEVEL; import java.io.IOException; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.TreeSet; import org.slf4j.LoggerFactory; import com.ibm.wala.classLoader.ClassLoaderImpl; import com.ibm.wala.classLoader.IClass; import com.ibm.wala.classLoader.IClassLoader; import com.ibm.wala.classLoader.Module; import com.ibm.wala.classLoader.ModuleEntry; import com.ibm.wala.ipa.cha.IClassHierarchy; import com.ibm.wala.types.ClassLoaderReference; import com.ibm.wala.types.TypeName; import com.ibm.wala.util.collections.HashCodeComparator; import com.ibm.wala.util.collections.HashSetFactory; import com.ibm.wala.util.collections.Iterator2Collection; import com.ibm.wala.util.config.SetOfClasses; import com.ibm.wala.util.warnings.Warning; import com.ibm.wala.util.warnings.Warnings; /** * ClassLoader for Java & Dalvik. * */ public class WDexClassLoaderImpl extends ClassLoaderImpl { private static final org.slf4j.Logger logger = LoggerFactory.getLogger(WDexClassLoaderImpl.class); private IClassLoader lParent; private final SetOfClasses exclusions; //Commented out until IBM fixes ClassLoaderFactoryImpl "protected IClassLoader makeNewClassLoader" // public WDexClassLoaderImpl(ClassLoaderReference loader, // ArrayClassLoader arrayClassLoader, IClassLoader parent, // SetOfClasses exclusions, IClassHierarchy cha) { // super(loader, arrayClassLoader, parent, exclusions, cha); // lParent = parent; // lExclusions = exclusions; // //DEBUG_LEVEL = 0; // } public WDexClassLoaderImpl(ClassLoaderReference loader,IClassLoader parent, SetOfClasses exclusions, IClassHierarchy cha) { super(loader, cha.getScope().getArrayClassLoader(), parent, exclusions, cha); lParent = parent; this.exclusions = exclusions; //DEBUG_LEVEL = 0; } @Override public void init(List<Module> modules) throws IOException { super.init(modules); // module are loaded according to the given order (same as in Java VM) Set<ModuleEntry> classModuleEntries = HashSetFactory.make(); for (Iterator<Module> it = modules.iterator(); it.hasNext();) { Module archive = it.next(); logger.debug("add archive: : "+archive); Set<ModuleEntry> classFiles = getDexFiles(archive); removeClassFiles(classFiles, classModuleEntries); loadAllDexClasses(classFiles); for (Iterator<ModuleEntry> it2 = classFiles.iterator(); it2.hasNext();) { ModuleEntry file = it2.next(); classModuleEntries.add(file); } } } /** * Remove from s any class file module entries which already are in t */ private void removeClassFiles(Set<ModuleEntry> s, Set<ModuleEntry> t) { Set<String> old = HashSetFactory.make(); for (Iterator<ModuleEntry> it = t.iterator(); it.hasNext();) { ModuleEntry m = it.next(); old.add(m.getClassName()); } HashSet<ModuleEntry> toRemove = HashSetFactory.make(); for (Iterator<ModuleEntry> it = s.iterator(); it.hasNext();) { ModuleEntry m = it.next(); if (old.contains(m.getClassName())) { toRemove.add(m); } } s.removeAll(toRemove); } private Set<ModuleEntry> getDexFiles(Module M) throws IOException { TreeSet<ModuleEntry> sortedEntries = new TreeSet<ModuleEntry>(HashCodeComparator.instance()); sortedEntries.addAll(Iterator2Collection.toSet(M.getEntries())); HashSet<ModuleEntry> result = HashSetFactory.make(); for (Iterator<ModuleEntry> it = sortedEntries.iterator(); it.hasNext();) { ModuleEntry entry = (ModuleEntry) it.next(); if (entry instanceof DexModuleEntry) { result.add(entry); } } return result; } private void loadAllDexClasses(Collection<ModuleEntry> moduleEntries) { for (Iterator<ModuleEntry> it = moduleEntries.iterator(); it.hasNext();) { ModuleEntry entry = it.next(); // Dalvik class if (entry instanceof DexModuleEntry) { DexModuleEntry dexEntry = ((DexModuleEntry) entry); String className = dexEntry.getClassName(); TypeName tName = TypeName.string2TypeName(className); //if (DEBUG_LEVEL > 0) { // System.err.println("Consider dex class: " + tName); //} //System.out.println("Typename: " + tName.toString()); //System.out.println(tName.getClassName()); if (loadedClasses.get(tName) != null) { Warnings.add(MultipleDexImplementationsWarning .create(className)); } else if (lParent != null && lParent.lookupClass(tName) != null) { Warnings.add(MultipleDexImplementationsWarning .create(className)); } //if the class is empty, ie an interface // else if (dexEntry.getClassDefItem().getClassData() == null) { // System.out.println("Jumping over (classdata null): "+dexEntry.getClassName()); // Warnings.add(MultipleDexImplementationsWarning // .create(dexEntry.getClassName())); // } else { IClass iClass = new DexIClass(this, cha, dexEntry); if (iClass.getReference().getName().equals(tName)) { // className is a descriptor, so strip the 'L' if (exclusions != null && exclusions.contains(className.substring(1))) { if (DEBUG_LEVEL > 0) { System.err.println("Excluding " + className); } continue; } logger.debug("Load class: " + className); loadedClasses.put(tName, iClass); } else { Warnings.add(InvalidDexFile.create(className)); } } } } } /** * @return the IClassHierarchy of this classLoader. */ public IClassHierarchy getClassHierarcy() { return cha; } /** * A warning when we find more than one implementation of a given class name */ private static class MultipleDexImplementationsWarning extends Warning { final String className; MultipleDexImplementationsWarning(String className) { super(Warning.SEVERE); this.className = className; } @Override public String getMsg() { return getClass().toString() + " : " + className; } public static MultipleDexImplementationsWarning create(String className) { return new MultipleDexImplementationsWarning(className); } } /** * A warning when we encounter InvalidClassFileException */ private static class InvalidDexFile extends Warning { final String className; InvalidDexFile(String className) { super(Warning.SEVERE); this.className = className; } @Override public String getMsg() { return getClass().toString() + " : " + className; } public static InvalidDexFile create(String className) { return new InvalidDexFile(className); } } }