/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package java.lang;
import gnu.classpath.Configuration;
import gnu.classpath.SystemProperties;
import gnu.java.lang.InstrumentationImpl;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.instrument.Instrumentation;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.ProtectionDomain;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.zip.ZipFile;
import org.jikesrvm.classloader.BootstrapClassLoader;
import org.jikesrvm.classloader.RVMClassLoader;
import org.jikesrvm.classloader.RVMType;
import org.jikesrvm.util.ImmutableEntryHashMapRVM;
/**
* Jikes RVM impl of VMClassLoader.
* See GNU classpath reference impl for javadoc.
*/
final class VMClassLoader {
/**
* A map of maps. The first map is indexed by the classloader. The
* map this finds then maps String class names to classes
*/
private static final ImmutableEntryHashMapRVM<ClassLoader,ImmutableEntryHashMapRVM<String,Class<?>>> loadedClasses =
new ImmutableEntryHashMapRVM<ClassLoader,ImmutableEntryHashMapRVM<String,Class<?>>>();
/**
* The map for the boot strap class loader that is often represented by null
*/
private static final ImmutableEntryHashMapRVM<String,Class<?>> bootStrapLoadedClasses =
new ImmutableEntryHashMapRVM<String,Class<?>>();
/** packages loaded by the bootstrap class loader */
private static final ImmutableEntryHashMapRVM<String,Package> definedPackages =
new ImmutableEntryHashMapRVM<String,Package>();
private static final ImmutableEntryHashMapRVM<String,ZipFile> bootjars =
new ImmutableEntryHashMapRVM<String,ZipFile>();
static {
String[] packages = getBootPackages();
if (packages != null) {
String specName =
SystemProperties.getProperty("java.specification.name");
String vendor =
SystemProperties.getProperty("java.specification.vendor");
String version =
SystemProperties.getProperty("java.specification.version");
for (String packageName : packages) {
final Package p = new Package(packageName,
specName,
vendor,
version,
"GNU Classpath",
"GNU",
Configuration.CLASSPATH_VERSION,
null,
null);
definedPackages.put(packageName, p);
}
}
}
static Class<?> defineClass(ClassLoader cl, String name,
byte[] data, int offset, int len,
ProtectionDomain pd)
throws ClassFormatError {
RVMType vmType = RVMClassLoader.defineClassInternal(name, data, offset, len, cl);
Class<?> ans = vmType.getClassForType();
JikesRVMSupport.setClassProtectionDomain(ans, pd);
ImmutableEntryHashMapRVM<String,Class<?>> mapForCL;
if (cl == null || cl == BootstrapClassLoader.getBootstrapClassLoader()) {
mapForCL = bootStrapLoadedClasses;
} else {
mapForCL = loadedClasses.get(cl);
if (mapForCL == null) {
mapForCL = new ImmutableEntryHashMapRVM<String,Class<?>>();
loadedClasses.put(cl, mapForCL);
}
}
mapForCL.put(ans.getName(), ans);
return ans;
}
static void resolveClass(Class<?> c) {
RVMType cls = JikesRVMSupport.getTypeForClass(c);
cls.prepareForFirstUse();
}
static Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
return BootstrapClassLoader.getBootstrapClassLoader().loadClass(name, resolve);
}
static URL getResource(String name) {
Enumeration<URL> e = getResources(name);
if (e.hasMoreElements())
return e.nextElement();
return null;
}
static Enumeration<URL> getResources(String name) {
StringTokenizer st = new StringTokenizer(
SystemProperties.getProperty("java.boot.class.path", "."),
File.pathSeparator);
LinkedList<URL> v = new LinkedList<URL>();
while (st.hasMoreTokens()) {
File file = new File(st.nextToken());
if (file.isDirectory()) {
try {
File f = new File(file, name);
if (!f.exists()) continue;
v.add(new URL("file://" + f.getAbsolutePath()));
} catch (MalformedURLException e) {
throw new Error(e);
}
} else if (file.isFile()) {
ZipFile zip;
synchronized (bootjars) {
zip = bootjars.get(file.getName());
}
if (zip == null) {
try {
zip = new ZipFile(file);
synchronized (bootjars) {
bootjars.put(file.getName(), zip);
}
} catch (IOException e) {
continue;
}
}
String zname = name.startsWith("/") ? name.substring(1) : name;
if (zip.getEntry(zname) == null)
continue;
try {
v.add(new URL("jar:file://" +
file.getAbsolutePath() + "!/" + zname));
} catch (MalformedURLException e) {
throw new Error(e);
}
}
}
return Collections.enumeration(v);
}
private static String[] getBootPackages() {
URL indexList = getResource("META-INF/INDEX.LIST");
if (indexList != null) {
try {
Set<String> packageSet = new HashSet<String>();
String line;
int lineToSkip = 3;
BufferedReader reader = new BufferedReader(new InputStreamReader(indexList.openStream()));
while ((line = reader.readLine()) != null) {
if (lineToSkip == 0) {
if (line.length() == 0) {
lineToSkip = 1;
} else {
packageSet.add(line.replace('/', '.'));
}
} else {
lineToSkip--;
}
}
reader.close();
return packageSet.toArray(new String[packageSet.size()]);
} catch (IOException e) {
return new String[0];
}
} else {
return new String[0];
}
}
static Package getPackage(String name) {
return definedPackages.get(name);
}
static Package[] getPackages() {
Package[] packages = new Package[definedPackages.size()];
Iterator<Package> it = definedPackages.valueIterator();
int idx = 0;
while (it.hasNext()) {
packages[idx++] = it.next();
}
return packages;
}
static Class<?> getPrimitiveClass(char type) {
RVMType t;
switch (type) {
case 'Z':
t = RVMType.BooleanType;
break;
case 'B':
t = RVMType.ByteType;
break;
case 'C':
t = RVMType.CharType;
break;
case 'D':
t = RVMType.DoubleType;
break;
case 'F':
t = RVMType.FloatType;
break;
case 'I':
t = RVMType.IntType;
break;
case 'J':
t = RVMType.LongType;
break;
case 'S':
t = RVMType.ShortType;
break;
case 'V':
t = RVMType.VoidType;
break;
default:
throw new NoClassDefFoundError("Invalid type specifier: " + type);
}
return t.getClassForType();
}
static boolean defaultAssertionStatus() {
return true;
}
@SuppressWarnings({"unchecked","unused"}) // TODO should this method be deleted ?
private static Map packageAssertionStatus() {
return new HashMap();
}
@SuppressWarnings({"unchecked","unused"}) // TODO should this method be deleted ?
private static Map classAssertionStatus() {
return new HashMap();
}
static ClassLoader getSystemClassLoader() {
return RVMClassLoader.getApplicationClassLoader();
}
static Class<?>[] getAllLoadedClasses() {
LinkedList<Class<?>> classList = new LinkedList<Class<?>>();
for (ImmutableEntryHashMapRVM<String,Class<?>> classes : loadedClasses.values()) {
for (Class<?> cl : classes.values()) {
classList.add(cl);
}
}
Class<?>[] result = new Class[classList.size()];
return classList.toArray(result);
}
static Class<?>[] getInitiatedClasses(ClassLoader classLoader) {
ImmutableEntryHashMapRVM<String,Class<?>> mapForCL = loadedClasses.get(classLoader);
if (mapForCL == null) return new Class[]{};
LinkedList<Class<?>> classList = new LinkedList<Class<?>>();
for (Class<?> cl : mapForCL.values())
classList.add(cl);
Class<?>[] result = new Class[classList.size()];
return classList.toArray(result);
}
static Class<?> findLoadedClass(ClassLoader cl, String name) {
ImmutableEntryHashMapRVM<String,Class<?>> mapForCL = loadedClasses.get(cl);
if (mapForCL == null) return null;
return mapForCL.get(name);
}
private static Instrumentation instrumenter = null;
static void setInstrumenter(Instrumentation theInstrumenter) {
instrumenter = theInstrumenter;
}
static Class<?> defineClassWithTransformers(ClassLoader loader, String name, byte[] data,
int offset, int len, ProtectionDomain pd) {
if (instrumenter != null) {
byte[] modifiedData = new byte[len];
System.arraycopy(data, offset, modifiedData, 0, len);
String jvmName = name.replace('.', '/');
modifiedData =
((InstrumentationImpl)instrumenter).callTransformers(loader, jvmName, null, pd, modifiedData);
return defineClass(loader, name, modifiedData, 0, modifiedData.length, pd);
} else {
return defineClass(loader, name, data, offset, len, pd);
}
}
}