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    }