Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* 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 com.ibm.wala.core.util.io.FileProvider;
import com.ibm.wala.core.util.io.FileSuffixes;
import com.ibm.wala.core.util.shrike.ShrikeClassReaderHandle;
import com.ibm.wala.core.util.strings.Atom;
import com.ibm.wala.core.util.warnings.Warning;
import com.ibm.wala.core.util.warnings.Warnings;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.shrike.shrikeCT.ClassReader;
import com.ibm.wala.shrike.shrikeCT.InvalidClassFileException;
import com.ibm.wala.ssa.SSAInstructionFactory;
import com.ibm.wala.types.ClassLoaderReference;
import com.ibm.wala.types.TypeName;
import com.ibm.wala.util.collections.HashMapFactory;
import com.ibm.wala.util.collections.HashSetFactory;
import com.ibm.wala.util.collections.Iterator2Iterable;
import com.ibm.wala.util.config.SetOfClasses;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarInputStream;
/** A class loader that reads class definitions from a set of Modules. */
public class ClassLoaderImpl implements IClassLoader {
public static final int DEBUG_LEVEL = 0;
private static final boolean OPTIMIZE_JAR_FILE_IO = true;
/** classes to ignore */
private final SetOfClasses exclusions;
/** Identity for this class loader */
private final ClassLoaderReference loader;
/** A mapping from class name (TypeName) to IClass */
protected final Map loadedClasses = HashMapFactory.make();
/** A mapping from class name (TypeName) to String (source file name) */
private final Map sourceMap = HashMapFactory.make();
/** Parent classloader */
private final IClassLoader parent;
/** Governing class hierarchy */
protected final IClassHierarchy cha;
/** an object to delegate to for loading of array classes */
private final ArrayClassLoader arrayClassLoader;
/**
* @param loader class loader reference identifying this loader
* @param parent parent loader for delegation
* @param exclusions set of classes to exclude from loading
*/
@SuppressWarnings("unused")
public ClassLoaderImpl(
ClassLoaderReference loader,
ArrayClassLoader arrayClassLoader,
IClassLoader parent,
SetOfClasses exclusions,
IClassHierarchy cha) {
if (loader == null) {
throw new IllegalArgumentException("null loader");
}
this.arrayClassLoader = arrayClassLoader;
this.parent = parent;
this.loader = loader;
this.exclusions = exclusions;
this.cha = cha;
if (DEBUG_LEVEL > 0) {
System.err.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
*/
@SuppressWarnings("unused")
private Set getSourceFiles(Module M) throws IOException {
if (DEBUG_LEVEL > 0) {
System.err.println("Get source files for " + M);
}
HashSet result = HashSetFactory.make();
for (ModuleEntry entry : Iterator2Iterable.make(M.getEntries())) {
if (DEBUG_LEVEL > 0) {
System.err.println("consider entry for source information: " + entry);
}
if (entry.isSourceFile()) {
if (DEBUG_LEVEL > 0) {
System.err.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
*/
@SuppressWarnings("unused")
private Set getClassFiles(Module M) throws IOException {
if (DEBUG_LEVEL > 0) {
System.err.println("Get class files for " + M);
}
HashSet result = HashSetFactory.make();
for (ModuleEntry entry : Iterator2Iterable.make(M.getEntries())) {
if (DEBUG_LEVEL > 0) {
System.err.println("ClassLoaderImpl.getClassFiles:Got entry: " + entry);
}
if (entry.isClassFile()) {
if (DEBUG_LEVEL > 0) {
System.err.println("result contains: " + entry);
}
result.add(entry);
} else if (entry.isModuleFile()) {
Set s = getClassFiles(entry.asModule());
removeClassFiles(s, result);
result.addAll(s);
} else {
if (DEBUG_LEVEL > 0) {
System.err.println("Ignoring entry: " + entry);
}
}
}
return result;
}
/** Remove from s any class file module entries which already are in t */
private static void removeClassFiles(Set s, Set t) {
s.removeAll(t);
}
/** Return a Set of IClasses, which represents all classes this class loader can load. */
private Collection getAllClasses() {
assert loadedClasses != null;
return loadedClasses.values();
}
static class ByteArrayReaderHandle extends ShrikeClassReaderHandle {
public ByteArrayReaderHandle(ModuleEntry entry, byte[] contents) {
super(entry);
assert contents != null && contents.length > 0;
this.contents = contents;
}
private byte[] contents;
private boolean cleared;
@Override
public ClassReader get() throws InvalidClassFileException {
if (cleared) {
return super.get();
} else {
return new ClassReader(contents);
}
}
@Override
public void clear() {
if (cleared) {
super.clear();
} else {
contents = null;
cleared = true;
}
}
}
/** Set up the set of classes loaded by this object. */
@SuppressWarnings("unused")
private void loadAllClasses(
Collection moduleEntries, Map fileContents, boolean isJMODType) {
for (ModuleEntry entry : moduleEntries) {
// java11 support for jmod files
if (!entry.isClassFile()
|| (isJMODType && entry.getClassName().startsWith("classes/module-info"))) {
continue;
}
@SuppressWarnings("NonConstantStringShouldBeStringBuffer")
String className = entry.getClassName().replace('.', '/');
// java11 support for jmod files
if (isJMODType && className.startsWith("classes/")) {
className = className.replace("classes/", "");
}
if (DEBUG_LEVEL > 0) {
System.err.println("Consider " + className);
}
if (exclusions != null && exclusions.contains(className)) {
if (DEBUG_LEVEL > 0) {
System.err.println("Excluding " + className);
}
continue;
}
ShrikeClassReaderHandle entryReader = new ShrikeClassReaderHandle(entry);
className = 'L' + className;
if (DEBUG_LEVEL > 0) {
System.err.println("Load class " + className);
}
try {
TypeName T = TypeName.string2TypeName(className);
if (loadedClasses.get(T) != null) {
Warnings.add(MultipleImplementationsWarning.create(className));
} else if (parent != null && parent.lookupClass(T) != null) {
Warnings.add(MultipleImplementationsWarning.create(className));
} else {
// try to read from memory
ShrikeClassReaderHandle reader = entryReader;
if (fileContents != null) {
final Object contents = fileContents.get(entry.getName());
if (contents != null) {
// reader that uses the in-memory bytes
reader = new ByteArrayReaderHandle(entry, (byte[]) contents);
}
}
ShrikeClass tmpKlass = new ShrikeClass(reader, this, cha);
if (tmpKlass.getReference().getName().equals(T)) {
// always used the reader based on the entry after this point,
// so we can null out and re-read class file contents
loadedClasses.put(T, new ShrikeClass(entryReader, this, cha));
if (DEBUG_LEVEL > 1) {
System.err.println("put " + T + ' ');
}
} else {
Warnings.add(InvalidClassFile.create(className));
}
}
} catch (InvalidClassFileException e) {
if (DEBUG_LEVEL > 0) {
System.err.println("Ignoring class " + className + " due to InvalidClassFileException");
}
Warnings.add(InvalidClassFile.create(className));
}
}
}
@SuppressWarnings("unused")
private Map getAllClassAndSourceFileContents(
byte[] jarFileContents, String fileName, Map> entrySizes) {
if (jarFileContents == null) {
return null;
}
Map entrySizesForFile = entrySizes.get(fileName);
if (entrySizesForFile == null) {
return null;
}
Map result = HashMapFactory.make();
try (final JarInputStream s =
new JarInputStream(new ByteArrayInputStream(jarFileContents), false)) {
JarEntry entry;
while ((entry = s.getNextJarEntry()) != null) {
byte[] entryBytes = getEntryBytes(entrySizesForFile.get(entry.getName()), s);
if (entryBytes == null) {
return null;
}
String name = entry.getName();
if (FileSuffixes.isJarFile(name) || FileSuffixes.isWarFile(name)) {
Map nestedResult =
getAllClassAndSourceFileContents(entryBytes, name, entrySizes);
if (nestedResult == null) {
return null;
}
for (Map.Entry nestedEntry : nestedResult.entrySet()) {
final String entryName = nestedEntry.getKey();
if (!result.containsKey(entryName)) {
result.put(entryName, nestedEntry.getValue());
}
}
} else if (FileSuffixes.isClassFile(name) || FileSuffixes.isSourceFile(name)) {
result.put(name, entryBytes);
}
}
} catch (IOException e) {
assert false;
}
return result;
}
private static byte[] getEntryBytes(Long size, InputStream is) throws IOException {
if (size == null) {
return null;
}
ByteArrayOutputStream S = new ByteArrayOutputStream();
int n = 0;
long count = 0;
byte[] buffer = new byte[1024];
while (n > -1 && count < size) {
n = is.read(buffer, 0, 1024);
if (n > -1) {
S.write(buffer, 0, n);
count += n;
}
}
return S.toByteArray();
}
/** A warning 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;
}
@Override
public String getMsg() {
return getClass().toString() + " : " + className;
}
public static MultipleImplementationsWarning create(String className) {
return new MultipleImplementationsWarning(className);
}
}
/** A warning when we encounter InvalidClassFileException */
private static class InvalidClassFile extends Warning {
final String className;
InvalidClassFile(String className) {
super(Warning.SEVERE);
this.className = className;
}
@Override
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 */
@SuppressWarnings("unused")
protected void loadAllSources(Set sourceModules) {
for (ModuleEntry entry : sourceModules) {
String className = entry.getClassName().replace('.', '/');
className = className.replace(File.separatorChar, '/');
className = 'L' + (className.startsWith("/") ? className.substring(1) : className);
TypeName T = TypeName.string2TypeName(className);
// Note: entry.getClassName() may not return the correct class name, for example,
// com.ibm.wala.classLoader.SourceFileModule.getClassName()
// {
// return FileSuffixes.stripSuffix(fileName).replace(File.separator.charAt(0), '/');
// }
// If fileName includes the full path, such as
// C:\TestApps\HelloWorld\src\main\java\com\ibm\helloworld\MyClass.java
// Then above method would return the class name as:
// C:/TestApps/HelloWorld/src/main/java/com/ibm/helloworld/MyClass
// However, in WALA, we represent class name as:
// PackageName.className, such as com.ibm.helloworld.MyClass
//
// In method "void init(List modules)", We loadAllClasses firstly, then
// loadAllSources. Therefore, all the classes should be in the map loadedClasses
// when this method loadAllSources is called. To ensure we add the correct class
// name to the sourceMap, here we look up the class name from the map "loadedClasses"
// before adding the source info to the sourceMap. If we could not find the class,
// we edit the className and try again.
boolean success = false;
if (loadedClasses.get(T) != null) {
if (DEBUG_LEVEL > 0) {
System.err.println("adding to source map: " + T + " -> " + entry.getName());
}
sourceMap.put(T, entry);
success = true;
}
// Class does not exist
else {
// look at substrings starting after '/' characters, in the hope
// that we find a known class name
while (className.indexOf('/') > 0) {
className = 'L' + className.substring(className.indexOf('/') + 1);
TypeName T2 = TypeName.string2TypeName(className);
if (loadedClasses.get(T2) != null) {
if (DEBUG_LEVEL > 0) {
System.err.println("adding to source map: " + T2 + " -> " + entry.getName());
}
sourceMap.put(T2, entry);
success = true;
break;
}
}
}
if (success == false) {
// Add the T and entry to the sourceMap anyway, just in case in some special
// cases, we add new classes later.
if (DEBUG_LEVEL > 0) {
System.err.println("adding to source map: " + T + " -> " + entry.getName());
}
sourceMap.put(T, entry);
}
}
}
/**
* Initialize internal data structures
*
* @throws IllegalArgumentException if modules is null
*/
@SuppressWarnings("unused")
@Override
public void init(List modules) throws IOException {
if (modules == null) {
throw new IllegalArgumentException("modules is null");
}
// module are loaded according to the given order (same as in Java VM)
Set classModuleEntries = HashSetFactory.make();
Set sourceModuleEntries = HashSetFactory.make();
for (Module archive : modules) {
boolean isJMODType = false;
if (archive instanceof JarFileModule) {
JarFile jarFile = ((JarFileModule) archive).getJarFile();
isJMODType = (jarFile != null) && jarFile.getName().endsWith(".jmod");
}
if (DEBUG_LEVEL > 0) {
System.err.println("add archive: " + archive);
}
// byte[] jarFileContents = null;
if (OPTIMIZE_JAR_FILE_IO && archive instanceof JarFileModule) {
// if we have a jar file, we read the whole thing into memory and operate on that; enables
// more
// efficient sequential I/O
// this is work in progress; for now, we read the file into memory and throw away the
// contents, which
// still gives a speedup for large jar files since it reads sequentially and warms up the FS
// cache. we get a small slowdown
// for smaller jar files or for jar files already in the FS cache. eventually, we should
// actually use the bytes read and eliminate the slowdown
// 11/22/10: I can't figure out a way to actually use the bytes without hurting performance.
// Apparently,
// extracting files from a jar stored in memory via a JarInputStream is really slow compared
// to using
// a JarFile. Will leave this as is for now. --MS
// jarFileContents = archive instanceof JarFileModule ? getJarFileContents((JarFileModule)
// archive) : null;
getJarFileContents((JarFileModule) archive);
}
Set classFiles = getClassFiles(archive);
removeClassFiles(classFiles, classModuleEntries);
Set sourceFiles = getSourceFiles(archive);
Map allClassAndSourceFileContents = null;
if (OPTIMIZE_JAR_FILE_IO) {
// work in progress --MS
// if (archive instanceof JarFileModule) {
// final JarFileModule jfModule = (JarFileModule) archive;
// final String name = jfModule.getJarFile().getName();
// Map> entrySizes = getEntrySizes(jfModule, name);
// allClassAndSourceFileContents = getAllClassAndSourceFileContents(jarFileContents, name,
// entrySizes);
// }
// jarFileContents = null;
}
loadAllClasses(classFiles, allClassAndSourceFileContents, isJMODType);
loadAllSources(sourceFiles);
classModuleEntries.addAll(classFiles);
sourceModuleEntries.addAll(sourceFiles);
}
}
@SuppressWarnings("unused")
private Map> getEntrySizes(Module module, String name) {
Map> result = HashMapFactory.make();
Map curFileResult = HashMapFactory.make();
for (ModuleEntry e : Iterator2Iterable.make(module.getEntries())) {
if (e.isModuleFile()) {
result.putAll(getEntrySizes(e.asModule(), e.getName()));
} else {
if (e instanceof JarFileEntry) {
curFileResult.put(e.getName(), ((JarFileEntry) e).getSize());
}
}
}
result.put(name, curFileResult);
return result;
}
/** get the contents of a jar file. if any IO exceptions occur, catch and return null. */
private static void getJarFileContents(JarFileModule archive) {
String jarFileName = archive.getJarFile().getName();
InputStream s = null;
try {
File jarFile = new FileProvider().getFile(jarFileName);
int bufferSize = 65536;
s = new BufferedInputStream(new FileInputStream(jarFile), bufferSize);
byte[] b = new byte[1024];
int n = s.read(b);
while (n != -1) {
n = s.read(b);
}
} catch (IOException e) {
} finally {
try {
if (s != null) {
s.close();
}
} catch (IOException e) {
}
}
}
@Override
public ClassLoaderReference getReference() {
return loader;
}
@Override
public Iterator iterateAllClasses() {
return getAllClasses().iterator();
}
@SuppressWarnings("unused")
@Override
public IClass lookupClass(TypeName className) {
if (className == null) {
throw new IllegalArgumentException("className is null");
}
if (DEBUG_LEVEL > 1) {
System.err.println(this + ": lookupClass " + className);
}
// treat arrays specially:
if (className.isArrayType()) {
return arrayClassLoader.lookupClass(className, this, cha);
}
// try delegating first.
IClassLoader parent = getParent();
if (parent != null) {
IClass result = parent.lookupClass(className);
if (result != null) {
return result;
}
}
// delegating failed. Try our own namespace.
IClass result = loadedClasses.get(className);
return result;
}
/** Method getParent. */
@Override
public IClassLoader getParent() {
return parent;
}
@Override
public Atom getName() {
return loader.getName();
}
@Override
public Language getLanguage() {
return Language.JAVA;
}
@Override
public String toString() {
return getName().toString();
}
@Override
public int getNumberOfClasses() {
return getAllClasses().size();
}
@Override
public int getNumberOfMethods() {
int result = 0;
for (IClass klass : Iterator2Iterable.make(iterateAllClasses())) {
result += klass.getDeclaredMethods().size();
}
return result;
}
@Override
public String getSourceFileName(IClass klass) {
if (klass == null) {
throw new IllegalArgumentException("klass is null");
}
ModuleEntry e = sourceMap.get(klass.getName());
return e == null ? null : e.getName();
}
@Override
public Reader getSource(IMethod method, int offset) {
return getSource(method.getDeclaringClass());
}
@Override
public String getSourceFileName(IMethod method, int offset) {
return getSourceFileName(method.getDeclaringClass());
}
@Override
public Reader getSource(IClass klass) {
if (klass == null) {
throw new IllegalArgumentException("klass is null");
}
ModuleEntry e = sourceMap.get(klass.getName());
return e == null ? null : new InputStreamReader(e.getInputStream());
}
@Override
public void removeAll(Collection toRemove) {
if (toRemove == null) {
throw new IllegalArgumentException("toRemove is null");
}
toRemove.stream().map(IClass::getName).peek(loadedClasses::remove).forEach(sourceMap::remove);
}
@Override
public SSAInstructionFactory getInstructionFactory() {
return getLanguage().instructionFactory();
}
}