001/* 002 * This file is part of the Jikes RVM project (http://jikesrvm.org). 003 * 004 * This file is licensed to You under the Eclipse Public License (EPL); 005 * You may not use this file except in compliance with the License. You 006 * may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/eclipse-1.0.php 009 * 010 * See the COPYRIGHT.txt file distributed with this work for information 011 * regarding copyright ownership. 012 */ 013package org.jikesrvm.classloader; 014 015import java.io.DataInputStream; 016import java.io.File; 017import java.io.FileInputStream; 018import java.io.IOException; 019import java.io.InputStream; 020import java.net.URL; 021import java.util.Enumeration; 022import java.util.HashMap; 023import java.util.StringTokenizer; 024import java.util.Vector; 025import java.util.zip.ZipEntry; 026import java.util.zip.ZipFile; 027import org.jikesrvm.VM; 028import org.jikesrvm.runtime.Entrypoints; 029import org.jikesrvm.util.ImmutableEntryHashMapRVM; 030 031/** 032 * Implements an object that functions as the bootstrap class loader. 033 * This class is a Singleton pattern. 034 */ 035public final class BootstrapClassLoader extends java.lang.ClassLoader { 036 037 private final ImmutableEntryHashMapRVM<String, RVMType> loaded = 038 new ImmutableEntryHashMapRVM<String, RVMType>(); 039 040 /** Places whence we load bootstrap .class files. */ 041 private static String bootstrapClasspath; 042 043 /** 044 * Set list of places to be searched for VM classes and resources. 045 * @param bootstrapClasspath path specification in standard "classpath" 046 * format 047 */ 048 public static void setBootstrapRepositories(String bootstrapClasspath) { 049 BootstrapClassLoader.bootstrapClasspath = bootstrapClasspath; 050 } 051 052 /** 053 * @return List of places to be searched for VM classes and resources, 054 * in standard "classpath" format 055 */ 056 public static String getBootstrapRepositories() { 057 return bootstrapClasspath; 058 } 059 060 /** 061 * Initialize for execution. 062 * @param bootstrapClasspath names of directories containing the bootstrap 063 * .class files, and the names of any .zip/.jar files. 064 * These are the ones that implement the VM and its 065 * standard runtime libraries. This may contain several names separated 066 * with colons (':'), just 067 * as a classpath may. (<code>null</code> ==> use the values specified by 068 * {@link #setBootstrapRepositories} when the boot image was created. This 069 * feature is not actually used, but may be helpful in avoiding trouble.) 070 */ 071 public static void boot(String bootstrapClasspath) { 072 if (bootstrapClasspath != null) { 073 BootstrapClassLoader.bootstrapClasspath = bootstrapClasspath; 074 } 075 zipFileCache = new HashMap<String, ZipFile>(); 076 if (VM.runningVM) { 077 try { 078 /* Here, we have to replace the fields that aren't carried over from 079 * boot image writing time to run time. 080 * This would be the following, if the fields weren't final: 081 * 082 * bootstrapClassLoader.definedPackages = new HashMap(); 083 */ 084 Entrypoints.classLoaderDefinedPackages.setObjectValueUnchecked(bootstrapClassLoader, 085 new java.util.HashMap<String, Package>()); 086 } catch (Exception e) { 087 VM.sysFail("Failed to setup bootstrap class loader"); 088 } 089 } 090 } 091 092 /** Prevent other classes from constructing one. */ 093 private BootstrapClassLoader() { 094 super(null); 095 } 096 097 /* Interface */ 098 private static final BootstrapClassLoader bootstrapClassLoader = new BootstrapClassLoader(); 099 100 public static BootstrapClassLoader getBootstrapClassLoader() { 101 return bootstrapClassLoader; 102 } 103 104 /** 105 * Backdoor for use by TypeReference.resolve when !VM.runningVM. 106 * As of this writing, it is not used by any other classes. 107 * @param className name of the class to be loaded 108 * @return type for the loaded class 109 * @throws NoClassDefFoundError when no definition of the class was found 110 */ 111 synchronized RVMType loadVMClass(String className) throws NoClassDefFoundError { 112 try { 113 InputStream is = getResourceAsStream(className.replace('.', File.separatorChar) + ".class"); 114 if (is == null) throw new NoClassDefFoundError(className); 115 DataInputStream dataInputStream = new DataInputStream(is); 116 RVMType type = null; 117 try { 118 // Debugging: 119 // VM.sysWriteln("loadVMClass: trying to resolve className " + className); 120 type = RVMClassLoader.defineClassInternal(className, dataInputStream, this); 121 loaded.put(className, type); 122 } finally { 123 try { 124 // Make sure the input stream is closed. 125 dataInputStream.close(); 126 } catch (IOException e) { } 127 } 128 return type; 129 } catch (NoClassDefFoundError e) { 130 throw e; 131 } catch (Throwable e) { 132 // We didn't find the class, or it wasn't valid, etc. 133 NoClassDefFoundError ncdf = new NoClassDefFoundError(className); 134 ncdf.initCause(e); 135 throw ncdf; 136 } 137 } 138 139 @Override 140 public synchronized Class<?> loadClass(String className, boolean resolveClass) throws ClassNotFoundException { 141 if (!VM.runningVM) { 142 return super.loadClass(className, resolveClass); 143 } 144 if (className.startsWith("L") && className.endsWith(";")) { 145 className = className.substring(1, className.length() - 2); 146 } 147 RVMType loadedType = loaded.get(className); 148 Class<?> loadedClass; 149 if (loadedType == null) { 150 loadedClass = findClass(className); 151 } else { 152 loadedClass = loadedType.getClassForType(); 153 } 154 if (resolveClass) { 155 resolveClass(loadedClass); 156 } 157 return loadedClass; 158 } 159 160 /** 161 * Search the bootstrap class loader's classpath for given class. 162 * 163 * @param className the name of the class to load 164 * @return the class object, if it was found 165 * @exception ClassNotFoundException if the class was not found, or was invalid 166 */ 167 @Override 168 public Class<?> findClass(String className) throws ClassNotFoundException { 169 final boolean DBG = false; 170 if (!VM.runningVM) { 171 return super.findClass(className); 172 } 173 if (className.startsWith("[")) { 174 TypeReference typeRef = 175 TypeReference.findOrCreate(this, Atom.findOrCreateAsciiAtom(className.replace('.', '/'))); 176 RVMType ans = typeRef.resolve(); 177 loaded.put(className, ans); 178 return ans.getClassForType(); 179 } else { 180 if (!VM.fullyBooted) { 181 VM.sysWrite("Trying to load a class ("); 182 VM.sysWrite(className); 183 VM.sysWrite(") too early in the booting process, before dynamic"); 184 VM.sysWriteln(" class loading is enabled; aborting."); 185 VM.sysFail("Trying to load a class too early in the booting process"); 186 } 187 // class types: try to find the class file 188 try { 189 if (className.startsWith("L") && className.endsWith(";")) { 190 className = className.substring(1, className.length() - 2); 191 } 192 InputStream is = getResourceAsStream(className.replace('.', File.separatorChar) + ".class"); 193 if (is == null) throw new ClassNotFoundException(className); 194 DataInputStream dataInputStream = new DataInputStream(is); 195 Class<?> cls = null; 196 try { 197 RVMType type = RVMClassLoader.defineClassInternal(className, dataInputStream, this); 198 loaded.put(className, type); 199 cls = type.getClassForType(); 200 } finally { 201 try { 202 // Make sure the input stream is closed. 203 dataInputStream.close(); 204 } catch (IOException e) { } 205 } 206 return cls; 207 } catch (ClassNotFoundException e) { 208 throw e; 209 } catch (Throwable e) { 210 if (DBG) { 211 VM.sysWrite("About to throw ClassNotFoundException(", className, ") because we got this Throwable:"); 212 e.printStackTrace(); 213 } 214 // We didn't find the class, or it wasn't valid, etc. 215 throw new ClassNotFoundException(className, e); 216 } 217 } 218 } 219 220 /** Keep this a static field, since it's looked at in 221 * {@link MemberReference#parse}. */ 222 public static final String myName = "BootstrapCL"; 223 224 @Override 225 public String toString() { 226 return myName; 227 } 228 229 private static HashMap<String, ZipFile> zipFileCache; 230 231 private interface Handler<T> { 232 void process(ZipFile zf, ZipEntry ze) throws Exception; 233 234 void process(File f) throws Exception; 235 236 T getResult(); 237 } 238 239 @Override 240 public InputStream getResourceAsStream(final String name) { 241 Handler<InputStream> findStream = new Handler<InputStream>() { 242 InputStream stream; 243 244 @Override 245 public InputStream getResult() { 246 return stream; 247 } 248 249 @Override 250 public void process(ZipFile zf, ZipEntry ze) throws Exception { 251 stream = zf.getInputStream(ze); 252 } 253 254 @Override 255 public void process(File file) throws Exception { 256 stream = new FileInputStream(file); 257 } 258 }; 259 260 return getResourceInternal(name, findStream, false); 261 } 262 263 @Override 264 public URL findResource(final String name) { 265 Handler<URL> findURL = new Handler<URL>() { 266 URL url; 267 268 @Override 269 public URL getResult() { 270 return url; 271 } 272 273 @Override 274 public void process(ZipFile zf, ZipEntry ze) throws Exception { 275 url = new URL("jar", null, -1, "file:" + zf.getName() + "!/" + name); 276 } 277 278 @Override 279 public void process(File file) throws Exception { 280 url = new URL("file", null, -1, file.getName()); 281 } 282 }; 283 284 return getResourceInternal(name, findURL, false); 285 } 286 287 @Override 288 public Enumeration<URL> findResources(final String name) { 289 Handler<Enumeration<URL>> findURL = new Handler<Enumeration<URL>>() { 290 Vector<URL> urls; 291 292 @Override 293 public Enumeration<URL> getResult() { 294 if (urls == null) urls = new Vector<URL>(); 295 return urls.elements(); 296 } 297 298 @Override 299 public void process(ZipFile zf, ZipEntry ze) throws Exception { 300 if (urls == null) urls = new Vector<URL>(); 301 urls.add(new URL("jar", null, -1, "file:" + zf.getName() + "!/" + name)); 302 } 303 304 @Override 305 public void process(File file) throws Exception { 306 if (urls == null) urls = new Vector<URL>(); 307 urls.add(new URL("file", null, -1, file.getName())); 308 } 309 }; 310 311 return getResourceInternal(name, findURL, true); 312 } 313 314 private <T> T getResourceInternal(String name, Handler<T> h, boolean multiple) { 315 if (name.startsWith(File.separator)) { 316 name = name.substring(File.separator.length()); 317 } 318 319 StringTokenizer tok = new StringTokenizer(getBootstrapRepositories(), File.pathSeparator); 320 321 while (tok.hasMoreElements()) { 322 try { 323 String path = tok.nextToken(); 324 if (path.endsWith(".jar") || path.endsWith(".zip")) { 325 ZipFile zf = zipFileCache.get(path); 326 if (zf == null) { 327 zf = new ZipFile(path); 328 zipFileCache.put(path, zf); 329 } 330 // Zip spec. states that separator must be '/' in the path 331 if (File.separatorChar != '/') { 332 name = name.replace(File.separatorChar, '/'); 333 } 334 ZipEntry ze = zf.getEntry(name); 335 if (ze == null) continue; 336 337 h.process(zf, ze); 338 if (!multiple) return h.getResult(); 339 } else if (path.endsWith(File.separator)) { 340 File file = new File(path + name); 341 if (file.exists()) { 342 h.process(file); 343 if (!multiple) return h.getResult(); 344 } 345 } else { 346 File file = new File(path + File.separator + name); 347 if (file.exists()) { 348 h.process(file); 349 if (!multiple) return h.getResult(); 350 } 351 } 352 } catch (Exception e) { 353 } 354 } 355 356 return (multiple) ? h.getResult() : null; 357 } 358}