WALA/com.ibm.wala.core/com.ibm.wala.core/src/com/ibm/wala/classLoader/ClassLoaderImpl.java

491 lines
15 KiB
Java

/*******************************************************************************
* Copyright (c) 2002 - 2006 IBM Corporation.
* All rights reserved. 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
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package com.ibm.wala.classLoader;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import com.ibm.wala.ipa.callgraph.impl.SetOfClasses;
import com.ibm.wala.ipa.cha.ClassHierarchy;
import com.ibm.wala.shrikeCT.InvalidClassFileException;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.TypeName;
import com.ibm.wala.util.Atom;
import com.ibm.wala.util.ShrikeClassReaderHandle;
import com.ibm.wala.util.collections.HashCodeComparator;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.collections.Iterator2Collection;
import com.ibm.wala.util.collections.ToStringComparator;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.debug.Trace;
import com.ibm.wala.util.warnings.Warning;
import com.ibm.wala.util.warnings.WarningSet;
/**
*
* A class loader that reads class definitions from a set of Modules.
*
* @author sfink
*/
public class ClassLoaderImpl implements IClassLoader {
private static final int DEBUG_LEVEL = 0;
/**
* classes to ignore
*/
private SetOfClasses exclusions;
/**
* Identity for this class loader
*/
private ClassLoaderReference loader;
/**
* A mapping from class name (TypeName) to IClass
*/
protected final Map<TypeName, IClass> loadedClasses = HashMapFactory.make();
/**
* A mapping from class name (TypeName) to String (source file name)
*/
private final Map<TypeName, String> sourceMap = HashMapFactory.make();
/**
* An object to track warnings
*/
private final WarningSet warnings;
/**
* Parent classloader
*/
private IClassLoader parent;
/**
* Governing class hierarchy
*/
protected final ClassHierarchy cha;
/**
* an object to delegate to for loading of array classes
*/
private final ArrayClassLoader arrayClassLoader;
/**
* Constructor for ModuleSetClassLoader.
*
* @param loader
* class loader reference identifying this loader
* @param parent
* parent loader for delegation
* @param exclusions
* set of classes to exclude from loading
*/
public ClassLoaderImpl(ClassLoaderReference loader, ArrayClassLoader arrayClassLoader, IClassLoader parent,
SetOfClasses exclusions, Set modules, ClassHierarchy cha, WarningSet warnings) {
this.arrayClassLoader = arrayClassLoader;
this.parent = parent;
this.loader = loader;
this.exclusions = exclusions;
this.cha = cha;
this.warnings = warnings;
if (DEBUG_LEVEL > 0) {
Trace.println("Creating class loader for " + loader);
}
}
/**
* Return the Set of (ModuleEntry) source files found in a module.
*
* @param M
* the module
* @return the Set of source files in the module
* @throws IOException
*/
private Set<ModuleEntry> getSourceFiles(Module M) throws IOException {
if (DEBUG_LEVEL > 0) {
Trace.println("Get source files for " + M);
}
TreeSet<ModuleEntry> sortedEntries = new TreeSet<ModuleEntry>(HashCodeComparator.instance());
sortedEntries.addAll(new Iterator2Collection<ModuleEntry>(M.getEntries()));
HashSet<ModuleEntry> result = HashSetFactory.make();
for (Iterator it = sortedEntries.iterator(); it.hasNext();) {
ModuleEntry entry = (ModuleEntry) it.next();
if (DEBUG_LEVEL > 0) {
Trace.println("consider entry for source information: " + entry);
}
if (entry.isSourceFile()) {
if (DEBUG_LEVEL > 0) {
Trace.println("found source file: " + entry);
}
result.add(entry);
} else if (entry.isModuleFile()) {
result.addAll(getSourceFiles(entry.asModule()));
}
}
return result;
}
/**
* Return the Set of (ModuleEntry) class files found in a module.
*
* @param M
* the module
* @return the Set of class Files in the module
* @throws IOException
*/
private Set<ModuleEntry> getClassFiles(Module M) throws IOException {
if (DEBUG_LEVEL > 0) {
Trace.println("Get class files for " + M);
}
TreeSet<ModuleEntry> sortedEntries = new TreeSet<ModuleEntry>(HashCodeComparator.instance());
sortedEntries.addAll(new Iterator2Collection<ModuleEntry>(M.getEntries()));
HashSet<ModuleEntry> result = HashSetFactory.make();
for (Iterator it = sortedEntries.iterator(); it.hasNext();) {
ModuleEntry entry = (ModuleEntry) it.next();
if (DEBUG_LEVEL > 0) {
Trace.println("ClassLoaderImpl.getClassFiles:Got entry: " + entry);
}
if (entry.isClassFile()) {
if (DEBUG_LEVEL > 0) {
Trace.println("result contains: " + entry);
}
result.add(entry);
} else if (entry.isModuleFile()) {
Set<ModuleEntry> s = getClassFiles(entry.asModule());
removeClassFiles(s, result);
result.addAll(s);
} else {
if (DEBUG_LEVEL > 0) {
Trace.println("Ignoring entry: " + entry);
}
}
}
return result;
}
/**
* Remove from s any class file module entries which already are in t
*
* @param s
* @param t
*/
private void removeClassFiles(Set<ModuleEntry> s, Set<ModuleEntry> t) {
Set<String> old = new HashSet<String>();
for (Iterator<ModuleEntry> it = t.iterator(); it.hasNext();) {
ModuleEntry m = it.next();
old.add(m.getClassName());
}
HashSet<ModuleEntry> toRemove = new HashSet<ModuleEntry>();
for (Iterator<ModuleEntry> it = s.iterator(); it.hasNext();) {
ModuleEntry m = it.next();
if (old.contains(m.getClassName())) {
toRemove.add(m);
}
}
s.removeAll(toRemove);
}
/**
* Return a Set of IClasses, which represents all classes this class loader
* can load.
*/
private Collection<IClass> getAllClasses() {
if (Assertions.verifyAssertions) {
Assertions._assert(loadedClasses != null);
}
return loadedClasses.values();
}
/**
* Set up the set of classes loaded by this object.
*/
private void loadAllClasses(Collection<ModuleEntry> moduleEntries) {
for (Iterator<ModuleEntry> it = moduleEntries.iterator(); it.hasNext();) {
ModuleEntry entry = it.next();
if (!entry.isClassFile()) {
continue;
}
String className = entry.getClassName().replace('.', '/');
if (DEBUG_LEVEL > 0) {
Trace.println("Consider " + className);
}
if (exclusions != null && exclusions.contains(className)) {
if (DEBUG_LEVEL > 0) {
Trace.println("Excluding " + className);
}
continue;
}
ShrikeClassReaderHandle reader = new ShrikeClassReaderHandle(entry);
className = "L" + className;
if (DEBUG_LEVEL > 0) {
Trace.println("Load class " + className);
}
try {
TypeName T = TypeName.string2TypeName(className);
if (loadedClasses.get(T) != null) {
warnings.add(MultipleImplementationsWarning.create(className));
} else {
ShrikeCTClassWrapper klass = new ShrikeCTClassWrapper(reader, this, cha, warnings);
if (klass.getReference().getName().equals(T)) {
loadedClasses.put(T, new ShrikeCTClassWrapper(reader, this, cha, warnings));
if (DEBUG_LEVEL > 1) {
Trace.println("put " + T + " ");
}
} else {
warnings.add(InvalidClassFile.create(className));
}
}
} catch (InvalidClassFileException e) {
if (DEBUG_LEVEL > 0) {
Trace.println("Ignoring class " + className + " due to InvalidClassFileException");
}
warnings.add(InvalidClassFile.create(className));
}
}
}
/**
* @author sfink
*
* A waring when we find more than one implementation of a given class name
*/
private static class MultipleImplementationsWarning extends Warning {
final String className;
MultipleImplementationsWarning(String className) {
super(Warning.SEVERE);
this.className = className;
}
public String getMsg() {
return getClass().toString() + " : " + className;
}
public static MultipleImplementationsWarning create(String className) {
return new MultipleImplementationsWarning(className);
}
}
/**
* @author sfink
*
* A waring when we encounter InvalidClassFileException
*/
private static class InvalidClassFile extends Warning {
final String className;
InvalidClassFile(String className) {
super(Warning.SEVERE);
this.className = className;
}
public String getMsg() {
return getClass().toString() + " : " + className;
}
public static InvalidClassFile create(String className) {
return new InvalidClassFile(className);
}
}
/**
* Set up mapping from type name to Module Entry
*/
protected void loadAllSources(Set<ModuleEntry> sourceModules) {
for (Iterator<ModuleEntry> it = sourceModules.iterator(); it.hasNext();) {
ModuleEntry entry = it.next();
String className = entry.getClassName().replace('.', '/');
className = className.replace(File.separatorChar, '/');
className = "L" + ((className.startsWith("/")) ? className.substring(1) : className);
TypeName T = TypeName.string2TypeName(className);
if (DEBUG_LEVEL > 0) {
Trace.println("adding to source map: " + T + " -> " + entry.getName());
}
sourceMap.put(T, entry.getName());
}
}
/**
* Initialize internal data structures
*
* @throws IOException
*/
public void init(Set modules) throws IOException {
// use tree set to keep things sorted ... for deterministic class loading
TreeSet<Module> archives = new TreeSet<Module>(ToStringComparator.instance());
for (Iterator i = modules.iterator(); i.hasNext();) {
Module M = (Module) i.next();
if (DEBUG_LEVEL > 0) {
Trace.println("add archive: " + M);
}
archives.add(M);
}
Set<ModuleEntry> classModuleEntries = new HashSet<ModuleEntry>();
Set<ModuleEntry> sourceModuleEntries = new HashSet<ModuleEntry>();
for (Iterator<Module> it = archives.iterator(); it.hasNext();) {
Module archive = it.next();
Set<ModuleEntry> classFiles = getClassFiles(archive);
removeClassFiles(classFiles, classModuleEntries);
for (Iterator<ModuleEntry> it2 = classFiles.iterator(); it2.hasNext();) {
ModuleEntry file = it2.next();
classModuleEntries.add(file);
}
Set<ModuleEntry> sourceFiles = getSourceFiles(archive);
for (Iterator<ModuleEntry> it2 = sourceFiles.iterator(); it2.hasNext();) {
ModuleEntry file = it2.next();
sourceModuleEntries.add(file);
}
}
loadAllClasses(classModuleEntries);
loadAllSources(sourceModuleEntries);
}
public ClassLoaderReference getReference() {
return loader;
}
public Iterator<IClass> iterateAllClasses() {
return getAllClasses().iterator();
}
/**
* This version returns null instead of throwing ClassNotFoundException.
*/
private IClass lookupClassInternal(TypeName className) {
if (DEBUG_LEVEL > 1) {
Trace.println(this + ": lookupClassInternal " + className);
}
// try delegating first.
ClassLoaderImpl parent = (ClassLoaderImpl) getParent();
if (parent != null) {
IClass result = parent.lookupClassInternal(className);
if (result != null)
return result;
}
// delegating failed. Try our own namespace.
return loadedClasses.get(className);
}
public IClass lookupClass(TypeName className) {
if (DEBUG_LEVEL > 1) {
Trace.println(this + ": lookupClass " + className);
}
// treat arrays specially:
if (className.isArrayType()) {
return arrayClassLoader.lookupClass(className, this);
}
// try delegating first.
ClassLoaderImpl parent = (ClassLoaderImpl) getParent();
if (parent != null) {
IClass result = parent.lookupClassInternal(className);
if (result != null) {
if (DEBUG_LEVEL > 1) {
Trace.println(this + ": returning class from parent: " + result);
}
return result;
}
}
// delegating failed. Try our own namespace.
IClass result = loadedClasses.get(className);
return result;
}
/**
* Method getParent.
*/
public IClassLoader getParent() {
return parent;
}
public Atom getName() {
return loader.getName();
}
/**
* @see java.lang.Object#toString()
*/
public String toString() {
return getName().toString();
}
/*
* (non-Javadoc)
*
* @see com.ibm.wala.classLoader.IClassLoader#getNumberOfClasses()
*/
public int getNumberOfClasses() {
return getAllClasses().size();
}
/*
* (non-Javadoc)
*
* @see com.ibm.wala.classLoader.IClassLoader#getNumberOfMethods()
*/
public int getNumberOfMethods() {
int result = 0;
for (Iterator<IClass> it = iterateAllClasses(); it.hasNext();) {
IClass klass = it.next();
for (Iterator i2 = klass.getDeclaredMethods(); i2.hasNext();) {
i2.next();
result++;
}
}
return result;
}
/*
* (non-Javadoc)
*
* @see com.ibm.wala.classLoader.IClassLoader#getSourceFileName(com.ibm.wala.classLoader.IClass)
*/
public String getSourceFileName(IClass klass) {
return sourceMap.get(klass.getName());
}
/*
* (non-Javadoc)
*
* @see com.ibm.wala.classLoader.IClassLoader#removeAll(java.util.Collection)
*/
public void removeAll(Collection<IClass> toRemove) {
for (Iterator<IClass> it = toRemove.iterator(); it.hasNext();) {
IClass klass = it.next();
loadedClasses.remove(klass.getName());
sourceMap.remove(klass.getName());
}
}
}