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 */ 013 package org.jikesrvm.classloader; 014 015 import java.io.DataInputStream; 016 import java.io.File; 017 import java.io.FileInputStream; 018 import java.io.IOException; 019 import java.io.InputStream; 020 import java.net.URL; 021 import java.util.Enumeration; 022 import java.util.HashMap; 023 import java.util.StringTokenizer; 024 import java.util.Vector; 025 import java.util.zip.ZipEntry; 026 import java.util.zip.ZipFile; 027 import org.jikesrvm.VM; 028 import org.jikesrvm.runtime.Entrypoints; 029 import 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 */ 035 public 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 * @throws NoClassDefFoundError 108 */ 109 synchronized RVMType loadVMClass(String className) throws NoClassDefFoundError { 110 try { 111 InputStream is = getResourceAsStream(className.replace('.', File.separatorChar) + ".class"); 112 if (is == null) throw new NoClassDefFoundError(className); 113 DataInputStream dataInputStream = new DataInputStream(is); 114 RVMType type = null; 115 try { 116 // Debugging: 117 // VM.sysWriteln("loadVMClass: trying to resolve className " + className); 118 type = RVMClassLoader.defineClassInternal(className, dataInputStream, this); 119 loaded.put(className, type); 120 } finally { 121 try { 122 // Make sure the input stream is closed. 123 dataInputStream.close(); 124 } catch (IOException e) { } 125 } 126 return type; 127 } catch (NoClassDefFoundError e) { 128 throw e; 129 } catch (Throwable e) { 130 // We didn't find the class, or it wasn't valid, etc. 131 NoClassDefFoundError ncdf = new NoClassDefFoundError(className); 132 ncdf.initCause(e); 133 throw ncdf; 134 } 135 } 136 137 @Override 138 public synchronized Class<?> loadClass(String className, boolean resolveClass) throws ClassNotFoundException { 139 if (!VM.runningVM) { 140 return super.loadClass(className, resolveClass); 141 } 142 if (className.startsWith("L") && className.endsWith(";")) { 143 className = className.substring(1, className.length() - 2); 144 } 145 RVMType loadedType = loaded.get(className); 146 Class<?> loadedClass; 147 if (loadedType == null) { 148 loadedClass = findClass(className); 149 } else { 150 loadedClass = loadedType.getClassForType(); 151 } 152 if (resolveClass) { 153 resolveClass(loadedClass); 154 } 155 return loadedClass; 156 } 157 158 /** 159 * Search the bootstrap class loader's classpath for given class. 160 * 161 * @param className the name of the class to load 162 * @return the class object, if it was found 163 * @exception ClassNotFoundException if the class was not found, or was invalid 164 */ 165 @Override 166 public Class<?> findClass(String className) throws ClassNotFoundException { 167 final boolean DBG=false; 168 if (!VM.runningVM) { 169 return super.findClass(className); 170 } 171 if (className.startsWith("[")) { 172 TypeReference typeRef = 173 TypeReference.findOrCreate(this, Atom.findOrCreateAsciiAtom(className.replace('.', '/'))); 174 RVMType ans = typeRef.resolve(); 175 loaded.put(className, ans); 176 return ans.getClassForType(); 177 } else { 178 if (!VM.fullyBooted) { 179 VM.sysWrite("Trying to load a class ("); 180 VM.sysWrite(className); 181 VM.sysWrite(") too early in the booting process, before dynamic"); 182 VM.sysWriteln(" class loading is enabled; aborting."); 183 VM.sysFail("Trying to load a class too early in the booting process"); 184 } 185 // class types: try to find the class file 186 try { 187 if (className.startsWith("L") && className.endsWith(";")) { 188 className = className.substring(1, className.length() - 2); 189 } 190 InputStream is = getResourceAsStream(className.replace('.', File.separatorChar) + ".class"); 191 if (is == null) throw new ClassNotFoundException(className); 192 DataInputStream dataInputStream = new DataInputStream(is); 193 Class<?> cls = null; 194 try { 195 RVMType type = RVMClassLoader.defineClassInternal(className, dataInputStream, this); 196 loaded.put(className, type); 197 cls = type.getClassForType(); 198 } finally { 199 try { 200 // Make sure the input stream is closed. 201 dataInputStream.close(); 202 } catch (IOException e) { } 203 } 204 return cls; 205 } catch (ClassNotFoundException e) { 206 throw e; 207 } catch (Throwable e) { 208 if (DBG) { 209 VM.sysWrite("About to throw ClassNotFoundException(", className, ") because we got this Throwable:"); 210 e.printStackTrace(); 211 } 212 // We didn't find the class, or it wasn't valid, etc. 213 throw new ClassNotFoundException(className, e); 214 } 215 } 216 } 217 218 /** Keep this a static field, since it's looked at in 219 * {@link MemberReference#parse}. */ 220 public static final String myName = "BootstrapCL"; 221 222 @Override 223 public String toString() { return myName; } 224 225 private static HashMap<String, ZipFile> zipFileCache; 226 227 private interface Handler<T> { 228 void process(ZipFile zf, ZipEntry ze) throws Exception; 229 230 void process(File f) throws Exception; 231 232 T getResult(); 233 } 234 235 @Override 236 public InputStream getResourceAsStream(final String name) { 237 Handler<InputStream> findStream = new Handler<InputStream>() { 238 InputStream stream; 239 240 @Override 241 public InputStream getResult() { return stream; } 242 243 @Override 244 public void process(ZipFile zf, ZipEntry ze) throws Exception { 245 stream = zf.getInputStream(ze); 246 } 247 248 @Override 249 public void process(File file) throws Exception { 250 stream = new FileInputStream(file); 251 } 252 }; 253 254 return getResourceInternal(name, findStream, false); 255 } 256 257 @Override 258 public URL findResource(final String name) { 259 Handler<URL> findURL = new Handler<URL>() { 260 URL url; 261 262 @Override 263 public URL getResult() { return url; } 264 265 @Override 266 public void process(ZipFile zf, ZipEntry ze) throws Exception { 267 url = new URL("jar", null, -1, "file:" + zf.getName() + "!/" + name); 268 } 269 270 @Override 271 public void process(File file) throws Exception { 272 url = new URL("file", null, -1, file.getName()); 273 } 274 }; 275 276 return getResourceInternal(name, findURL, false); 277 } 278 279 @Override 280 public Enumeration<URL> findResources(final String name) { 281 Handler<Enumeration<URL>> findURL = new Handler<Enumeration<URL>>() { 282 Vector<URL> urls; 283 284 @Override 285 public Enumeration<URL> getResult() { 286 if (urls == null) urls = new Vector<URL>(); 287 return urls.elements(); 288 } 289 290 @Override 291 public void process(ZipFile zf, ZipEntry ze) throws Exception { 292 if (urls == null) urls = new Vector<URL>(); 293 urls.addElement(new URL("jar", null, -1, "file:" + zf.getName() + "!/" + name)); 294 } 295 296 @Override 297 public void process(File file) throws Exception { 298 if (urls == null) urls = new Vector<URL>(); 299 urls.addElement(new URL("file", null, -1, file.getName())); 300 } 301 }; 302 303 return getResourceInternal(name, findURL, true); 304 } 305 306 private <T> T getResourceInternal(String name, Handler<T> h, boolean multiple) { 307 if (name.startsWith(File.separator)) { 308 name = name.substring(File.separator.length()); 309 } 310 311 StringTokenizer tok = new StringTokenizer(getBootstrapRepositories(), File.pathSeparator); 312 313 while (tok.hasMoreElements()) { 314 try { 315 String path = tok.nextToken(); 316 if (path.endsWith(".jar") || path.endsWith(".zip")) { 317 ZipFile zf = zipFileCache.get(path); 318 if (zf == null) { 319 zf = new ZipFile(path); 320 if (zf == null) { 321 continue; 322 } else { 323 zipFileCache.put(path, zf); 324 } 325 } 326 // Zip spec. states that separator must be '/' in the path 327 if (File.separatorChar != '/') { 328 name = name.replace(File.separatorChar, '/'); 329 } 330 ZipEntry ze = zf.getEntry(name); 331 if (ze == null) continue; 332 333 h.process(zf, ze); 334 if (!multiple) return h.getResult(); 335 } else if (path.endsWith(File.separator)) { 336 File file = new File(path + name); 337 if (file.exists()) { 338 h.process(file); 339 if (!multiple) return h.getResult(); 340 } 341 } else { 342 File file = new File(path + File.separator + name); 343 if (file.exists()) { 344 h.process(file); 345 if (!multiple) return h.getResult(); 346 } 347 } 348 } catch (Exception e) { 349 } 350 } 351 352 return (multiple) ? h.getResult() : null; 353 } 354 }