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