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.ByteArrayInputStream; 016 import java.io.DataInputStream; 017 import java.io.IOException; 018 import java.io.InputStream; 019 import org.jikesrvm.VM; 020 import org.jikesrvm.Constants; 021 import org.jikesrvm.Properties; 022 023 /** 024 * Manufacture type descriptions as needed by the running virtual machine. <p> 025 */ 026 public class RVMClassLoader implements Constants, ClassLoaderConstants { 027 028 private static final boolean DBG_APP_CL = false; 029 030 private static ClassLoader appCL; 031 032 /** 033 * Set list of places to be searched for application classes and resources. 034 * Do NOT set the java.class.path property; it is probably too early in 035 * the VM's booting cycle to set properties. 036 * 037 * @param classpath path specification in standard "classpath" format 038 */ 039 public static void stashApplicationRepositories(String classpath) { 040 if (DBG_APP_CL) { 041 VM.sysWriteln("RVMClassLoader.stashApplicationRepositories: " + "applicationRepositories = ", classpath); 042 } 043 /* If mis-initialized, trash it. */ 044 if (appCL != null && !classpath.equals(applicationRepositories)) { 045 appCL = null; 046 if (DBG_APP_CL) { 047 VM.sysWriteln("RVMClassLoader.stashApplicationRepositories: Wiping out my remembered appCL."); 048 } 049 } 050 applicationRepositories = classpath; 051 } 052 053 /** 054 * Are Java 1.4 style assertions enabled? 055 */ 056 private static boolean assertionsEnabled = false; 057 /** 058 * String describing packages and classes to enable assertions on (of the form ":<packagename>...|:<classname>") 059 */ 060 private static String[] enabledAssertionStrings; 061 /** 062 * String describing packages and classes to disable assertions on (of the form ":<packagename>...|:<classname>") 063 */ 064 private static String[] disabledAssertionStrings; 065 066 /** 067 * Remember the given enable assertions string 068 * @param arg String of the form ":<packagename>...|:<classname>" 069 */ 070 public static void stashEnableAssertionArg(String arg) { 071 assertionsEnabled = true; 072 enabledAssertionStrings = arg.split(":"); 073 if (enabledAssertionStrings != null) { 074 if ((enabledAssertionStrings.length == 0) || 075 (enabledAssertionStrings.length == 1 && enabledAssertionStrings[0].equals(""))) { 076 // force enabled assertion strings to null when no arguments are passed with -ea 077 enabledAssertionStrings = null; 078 } 079 } 080 } 081 082 /** 083 * Remember the given disable assertions string 084 * @param arg String of the form ":<packagename>...|:<classname>" 085 */ 086 public static void stashDisableAssertionArg(String arg) { 087 if (arg == null || arg.equals("")) { 088 assertionsEnabled = false; 089 } else { 090 disabledAssertionStrings = arg.split(":"); 091 } 092 } 093 094 /** 095 * Calculate the desired assertion status for a freshly loaded class 096 * @param klass to check against command line argument 097 * @return whether assertions should be enabled on class 098 */ 099 static boolean getDesiredAssertionStatus(RVMClass klass) { 100 if (!assertionsEnabled) { 101 // trivial - no assertions are enabled 102 return false; 103 } else { 104 if (enabledAssertionStrings == null && disabledAssertionStrings == null) { 105 // assertions enabled unconditionally 106 return true; 107 } else { 108 // search command line arguments to see if assertions are enabled 109 boolean result = false; 110 if(enabledAssertionStrings != null) { 111 for (String s : enabledAssertionStrings) { 112 if (s.equals(klass.getTypeRef().getName().classNameFromDescriptor()) || 113 klass.getPackageName().startsWith(s)) { 114 result = true; 115 break; 116 } 117 } 118 } 119 if (disabledAssertionStrings != null) { 120 for (String s : disabledAssertionStrings) { 121 if (s.equals(klass.getTypeRef().getName().classNameFromDescriptor()) || 122 klass.getPackageName().startsWith(s)) { 123 result = false; 124 break; 125 } 126 } 127 } 128 return result; 129 } 130 } 131 } 132 133 /** 134 * Set list of places to be searched for application classes and resources.<p> 135 * 136 * Our Jikes RVM classloader can not handle having the class path reset 137 * after it's been set up. Unfortunately, it is stashed by classes in 138 * GNU Classpath. 139 * 140 * @param classpath path specification in standard "classpath" format 141 */ 142 public static void setApplicationRepositories(String classpath) { 143 System.setProperty("java.class.path", classpath); 144 stashApplicationRepositories(classpath); 145 if (DBG_APP_CL) { 146 VM.sysWriteln("RVMClassLoader.setApplicationRepositories: applicationRepositories = ", applicationRepositories); 147 } 148 } 149 150 /** 151 * Get list of places currently being searched for application 152 * classes and resources. 153 * @return names of directories, .zip files, and .jar files 154 */ 155 public static String getApplicationRepositories() { 156 return applicationRepositories; 157 } 158 159 /** Are we getting the application CL? Access is synchronized via the 160 * Class object. Probably not necessary, but doesn't hurt, or shouldn't. 161 * Used for sanity checks. */ 162 private static int gettingAppCL = 0; 163 164 /** Is the application class loader ready for use? Don't leak it out until 165 * it is! */ 166 private static boolean appCLReady; 167 168 public static void declareApplicationClassLoaderIsReady() { 169 appCLReady = true; 170 } 171 172 public static ClassLoader getApplicationClassLoader() { 173 if (!VM.runningVM) { 174 return null; 175 } /* trick the boot image writer with null, 176 which it will use when initializing 177 java.lang.ClassLoader$StaticData */ 178 179 /* Lie, until we are really ready for someone to actually try 180 * to use this class loader to load classes and resources. 181 */ 182 if (!appCLReady) { 183 return BootstrapClassLoader.getBootstrapClassLoader(); 184 } 185 186 if (appCL != null) { 187 return appCL; 188 } 189 190 // Sanity Checks: 191 // synchronized (this) { 192 if (gettingAppCL > 0 || DBG_APP_CL) { 193 VM.sysWriteln("JikesRVM: RVMClassLoader.getApplicationClassLoader(): ", 194 gettingAppCL > 0 ? "Recursively " : "", 195 "invoked with ", 196 gettingAppCL, 197 " previous instances pending"); 198 } 199 if (gettingAppCL > 0) { 200 VM.sysFail( 201 "JikesRVM: While we are setting up the application class loader, some class required that selfsame application class loader. This is a chicken-and-egg problem; see a Jikes RVM Guru."); 202 } 203 ++gettingAppCL; 204 205 String r = getApplicationRepositories(); 206 207 if (Properties.verboseBoot >= 1 || DBG_APP_CL) { 208 VM.sysWriteln("RVMClassLoader.getApplicationClassLoader(): " + 209 "Initializing Application ClassLoader, with" + 210 " repositories: `", r, "'..."); 211 } 212 213 appCL = new ApplicationClassLoader(r); 214 215 if (Properties.verboseBoot >= 1 || DBG_APP_CL) { 216 VM.sysWriteln("RVMClassLoader.getApplicationClassLoader(): ...initialized Application classloader, to ", 217 appCL.toString()); 218 } 219 --gettingAppCL; 220 // } 221 return appCL; 222 } 223 224 //----------------// 225 // implementation // 226 //----------------// 227 228 // 229 private static String applicationRepositories; 230 231 // Names of special methods. 232 // 233 /** {@code <clinit>} */ 234 public static final Atom StandardClassInitializerMethodName = Atom.findOrCreateAsciiAtom("<clinit>"); 235 /** "()V" */ 236 public static final Atom StandardClassInitializerMethodDescriptor = Atom.findOrCreateAsciiAtom("()V"); 237 238 /** {@code <init>} */ 239 public static final Atom StandardObjectInitializerMethodName = Atom.findOrCreateAsciiAtom("<init>"); 240 /** {@code ()V} */ 241 public static final Atom StandardObjectInitializerMethodDescriptor = Atom.findOrCreateAsciiAtom("()V"); 242 /** {@code this} */ 243 public static final Atom StandardObjectInitializerHelperMethodName = Atom.findOrCreateAsciiAtom("this"); 244 245 /** {@code finalize} */ 246 public static final Atom StandardObjectFinalizerMethodName = Atom.findOrCreateAsciiAtom("finalize"); 247 /** {@code ()V} */ 248 public static final Atom StandardObjectFinalizerMethodDescriptor = Atom.findOrCreateAsciiAtom("()V"); 249 250 // Names of .class file attributes. 251 // 252 /** {@code Code} */ 253 static final Atom codeAttributeName = Atom.findOrCreateAsciiAtom("Code"); 254 /** {@code ConstantValue} */ 255 static final Atom constantValueAttributeName = Atom.findOrCreateAsciiAtom("ConstantValue"); 256 /** {@code LineNumberTable} */ 257 static final Atom lineNumberTableAttributeName = Atom.findOrCreateAsciiAtom("LineNumberTable"); 258 /** {@code Exceptions} */ 259 static final Atom exceptionsAttributeName = Atom.findOrCreateAsciiAtom("Exceptions"); 260 /** {@code SourceFile} */ 261 static final Atom sourceFileAttributeName = Atom.findOrCreateAsciiAtom("SourceFile"); 262 /** {@code LocalVariableTable} */ 263 static final Atom localVariableTableAttributeName = Atom.findOrCreateAsciiAtom("LocalVariableTable"); 264 /** {@code Deprecated} */ 265 static final Atom deprecatedAttributeName = Atom.findOrCreateAsciiAtom("Deprecated"); 266 /** {@code InnerClasses} */ 267 static final Atom innerClassesAttributeName = Atom.findOrCreateAsciiAtom("InnerClasses"); 268 /** {@code Synthetic} */ 269 static final Atom syntheticAttributeName = Atom.findOrCreateAsciiAtom("Synthetic"); 270 /** {@code EnclosingMethod} */ 271 static final Atom enclosingMethodAttributeName = Atom.findOrCreateAsciiAtom("EnclosingMethod"); 272 /** {@code Signature} */ 273 static final Atom signatureAttributeName = Atom.findOrCreateAsciiAtom("Signature"); 274 /** {@code RuntimeVisibleAnnotations} */ 275 static final Atom runtimeVisibleAnnotationsAttributeName = 276 Atom.findOrCreateAsciiAtom("RuntimeVisibleAnnotations"); 277 /** {@code RuntimeInvisibleAnnotations} */ 278 static final Atom runtimeInvisibleAnnotationsAttributeName = 279 Atom.findOrCreateAsciiAtom("RuntimeInvisibleAnnotations"); 280 /** {@code RuntimeVisibleParameterAnnotations} */ 281 static final Atom runtimeVisibleParameterAnnotationsAttributeName = 282 Atom.findOrCreateAsciiAtom("RuntimeVisibleParameterAnnotations"); 283 /** {@code RuntimeInvisibleParameterAnnotations} */ 284 static final Atom runtimeInvisibleParameterAnnotationsAttributeName = 285 Atom.findOrCreateAsciiAtom("RuntimeInvisibleParameterAnnotations"); 286 /** {@code AnnotationDefault} */ 287 static final Atom annotationDefaultAttributeName = Atom.findOrCreateAsciiAtom("AnnotationDefault"); 288 289 /** Initialize at boot time. 290 */ 291 public static void boot() { 292 appCL = null; 293 } 294 295 /** 296 * Initialize for boot image writing 297 */ 298 public static void init(String bootstrapClasspath) { 299 // specify where the VM's core classes and resources live 300 // 301 applicationRepositories = "."; // Carried over. 302 BootstrapClassLoader.boot(bootstrapClasspath); 303 } 304 305 public static RVMType defineClassInternal(String className, byte[] classRep, int offset, int length, 306 ClassLoader classloader) throws ClassFormatError { 307 return defineClassInternal(className, new ByteArrayInputStream(classRep, offset, length), classloader); 308 } 309 310 public static RVMType defineClassInternal(String className, InputStream is, ClassLoader classloader) 311 throws ClassFormatError { 312 TypeReference tRef; 313 if (className == null) { 314 // NUTS: Our caller hasn't bothered to tell us what this class is supposed 315 // to be called, so we must read the input stream and discover it ourselves 316 // before we actually can create the RVMClass instance. 317 try { 318 is.mark(is.available()); 319 tRef = getClassTypeRef(new DataInputStream(is), classloader); 320 is.reset(); 321 } catch (IOException e) { 322 ClassFormatError cfe = new ClassFormatError(e.getMessage()); 323 cfe.initCause(e); 324 throw cfe; 325 } 326 } else { 327 Atom classDescriptor = Atom.findOrCreateAsciiAtom(className.replace('.', '/')).descriptorFromClassName(); 328 tRef = TypeReference.findOrCreate(classloader, classDescriptor); 329 } 330 331 try { 332 if (VM.VerifyAssertions) VM._assert(tRef.isClassType()); 333 if (VM.TraceClassLoading && VM.runningVM) { 334 VM.sysWriteln("loading \"" + tRef.getName() + "\" with " + classloader); 335 } 336 RVMClass ans = ClassFileReader.readClass(tRef, new DataInputStream(is)); 337 tRef.setType(ans); 338 return ans; 339 } catch (IOException e) { 340 ClassFormatError cfe = new ClassFormatError(e.getMessage()); 341 cfe.initCause(e); 342 throw cfe; 343 } 344 } 345 346 // Shamelessly cloned & owned from ClassFileReader.readClass constructor.... 347 private static TypeReference getClassTypeRef(DataInputStream input, ClassLoader cl) 348 throws IOException, ClassFormatError { 349 int magic = input.readInt(); 350 if (magic != 0xCAFEBABE) { 351 throw new ClassFormatError("bad magic number " + Integer.toHexString(magic)); 352 } 353 354 // Drop class file version number on floor. readClass will do the check later. 355 input.readUnsignedShort(); // minor ID 356 input.readUnsignedShort(); // major ID 357 358 // 359 // pass 1: read constant pool 360 // 361 int[] constantPool = new int[input.readUnsignedShort()]; 362 byte[] tmpTags = new byte[constantPool.length]; 363 364 // note: slot 0 is unused 365 for (int i = 1; i < constantPool.length; i++) { 366 tmpTags[i] = input.readByte(); 367 switch (tmpTags[i]) { 368 case TAG_UTF: { 369 byte[] utf = new byte[input.readUnsignedShort()]; 370 input.readFully(utf); 371 constantPool[i] = Atom.findOrCreateUtf8Atom(utf).getId(); 372 break; 373 } 374 375 case TAG_UNUSED: 376 break; 377 378 case TAG_INT: 379 case TAG_FLOAT: 380 case TAG_FIELDREF: 381 case TAG_METHODREF: 382 case TAG_INTERFACE_METHODREF: 383 case TAG_MEMBERNAME_AND_DESCRIPTOR: 384 input.readInt(); // drop on floor 385 break; 386 387 case TAG_LONG: 388 case TAG_DOUBLE: 389 i++; 390 input.readLong(); // drop on floor 391 break; 392 393 case TAG_TYPEREF: 394 constantPool[i] = input.readUnsignedShort(); 395 break; 396 397 case TAG_STRING: 398 input.readUnsignedShort(); // drop on floor 399 break; 400 401 default: 402 throw new ClassFormatError("bad constant pool entry: " + tmpTags[i]); 403 } 404 } 405 406 // 407 // pass 2: post-process type constant pool entries 408 // (we must do this in a second pass because of forward references) 409 // 410 for (int i = 1; i < constantPool.length; i++) { 411 switch (tmpTags[i]) { 412 case TAG_LONG: 413 case TAG_DOUBLE: 414 ++i; 415 break; 416 417 case TAG_TYPEREF: { // in: utf index 418 Atom typeName = Atom.getAtom(constantPool[constantPool[i]]); 419 constantPool[i] = TypeReference.findOrCreate(cl, typeName.descriptorFromClassName()).getId(); 420 break; 421 } // out: type reference id 422 } 423 } 424 425 // drop modifiers on floor. 426 input.readUnsignedShort(); 427 428 int myTypeIndex = input.readUnsignedShort(); 429 return TypeReference.getTypeRef(constantPool[myTypeIndex]); 430 } 431 432 433 /** 434 * An unpleasant hack to deal with the problem of replaying work 435 * when using dynamic classloaders (whose identity will vary from 436 * run to run). When we can't find the classloader that was specified, 437 * see if there are any other (non-matching) classloaders that 438 * have the relevant class loaded. 439 * 440 * @param clazz The class we're after 441 * @return A usable classloader or null 442 */ 443 public static ClassLoader findWorkableClassloader(Atom clazz) { 444 for (ClassLoader clx: TypeReference.getCLDict()) { 445 TypeReference tRef = TypeReference.findOrCreate(clx, clazz); 446 RVMClass cls = (RVMClass) tRef.peekType(); 447 448 if (cls != null) 449 return clx; 450 else { 451 try { 452 cls = tRef.resolve().asClass(); 453 cls.resolve(); 454 cls.instantiate(); 455 cls.initialize(); 456 return clx; 457 } catch (NoClassDefFoundError cnf) { 458 /* silently catch this exception and try another class loader */ 459 } 460 } 461 } 462 return null; 463 } 464 }