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.
135 * @param classpath path specification in standard "classpath" format
136 *
137 * Our Jikes RVM classloader can not handle having the class path reset
138 * after it's been set up. Unfortunately, it is stashed by classes in
139 * GNU Classpath.
140 */
141 public static void setApplicationRepositories(String classpath) {
142 System.setProperty("java.class.path", classpath);
143 stashApplicationRepositories(classpath);
144 if (DBG_APP_CL) {
145 VM.sysWriteln("RVMClassLoader.setApplicationRepositories: applicationRepositories = ", applicationRepositories);
146 }
147 }
148
149 /**
150 * Get list of places currently being searched for application
151 * classes and resources.
152 * @return names of directories, .zip files, and .jar files
153 */
154 public static String getApplicationRepositories() {
155 return applicationRepositories;
156 }
157
158 /** Are we getting the application CL? Access is synchronized via the
159 * Class object. Probably not necessary, but doesn't hurt, or shouldn't.
160 * Used for sanity checks. */
161
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 /** "<clinit>" */
234 public static final Atom StandardClassInitializerMethodName = Atom.findOrCreateAsciiAtom("<clinit>");
235 /** "()V" */
236 public static final Atom StandardClassInitializerMethodDescriptor = Atom.findOrCreateAsciiAtom("()V");
237
238 /** "<init>" */
239 public static final Atom StandardObjectInitializerMethodName = Atom.findOrCreateAsciiAtom("<init>");
240 /** "()V" */
241 public static final Atom StandardObjectInitializerMethodDescriptor = Atom.findOrCreateAsciiAtom("()V");
242 /** "this" */
243 public static final Atom StandardObjectInitializerHelperMethodName = Atom.findOrCreateAsciiAtom("this");
244
245 /** "finalize" */
246 public static final Atom StandardObjectFinalizerMethodName = Atom.findOrCreateAsciiAtom("finalize");
247 /** "()V" */
248 public static final Atom StandardObjectFinalizerMethodDescriptor = Atom.findOrCreateAsciiAtom("()V");
249
250 // Names of .class file attributes.
251 //
252 /** "Code" */
253 static final Atom codeAttributeName = Atom.findOrCreateAsciiAtom("Code");
254 /** "ConstantValue" */
255 static final Atom constantValueAttributeName = Atom.findOrCreateAsciiAtom("ConstantValue");
256 /** "LineNumberTable" */
257 static final Atom lineNumberTableAttributeName = Atom.findOrCreateAsciiAtom("LineNumberTable");
258 /** "Exceptions" */
259 static final Atom exceptionsAttributeName = Atom.findOrCreateAsciiAtom("Exceptions");
260 /** "SourceFile" */
261 static final Atom sourceFileAttributeName = Atom.findOrCreateAsciiAtom("SourceFile");
262 /** "LocalVariableTable" */
263 static final Atom localVariableTableAttributeName = Atom.findOrCreateAsciiAtom("LocalVariableTable");
264 /** "Deprecated" */
265 static final Atom deprecatedAttributeName = Atom.findOrCreateAsciiAtom("Deprecated");
266 /** "InnerClasses" */
267 static final Atom innerClassesAttributeName = Atom.findOrCreateAsciiAtom("InnerClasses");
268 /** "Synthetic" */
269 static final Atom syntheticAttributeName = Atom.findOrCreateAsciiAtom("Synthetic");
270 /** "EnclosingMethod" */
271 static final Atom enclosingMethodAttributeName = Atom.findOrCreateAsciiAtom("EnclosingMethod");
272 /** "Signature" */
273 static final Atom signatureAttributeName = Atom.findOrCreateAsciiAtom("Signature");
274 /** "RuntimeVisibleAnnotations" */
275 static final Atom runtimeVisibleAnnotationsAttributeName =
276 Atom.findOrCreateAsciiAtom("RuntimeVisibleAnnotations");
277 /** "RuntimeInvisibleAnnotations" */
278 static final Atom runtimeInvisibleAnnotationsAttributeName =
279 Atom.findOrCreateAsciiAtom("RuntimeInvisibleAnnotations");
280 /** "RuntimeVisibleParameterAnnotations" */
281 static final Atom runtimeVisibleParameterAnnotationsAttributeName =
282 Atom.findOrCreateAsciiAtom("RuntimeVisibleParameterAnnotations");
283 /** "RuntimeInvisibleParameterAnnotations" */
284 static final Atom runtimeInvisibleParameterAnnotationsAttributeName =
285 Atom.findOrCreateAsciiAtom("RuntimeInvisibleParameterAnnotations");
286 /** "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 }