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    
014    package org.jikesrvm.classloader;
015    
016    import java.io.DataInputStream;
017    import java.io.IOException;
018    
019    import org.jikesrvm.Constants;
020    import org.jikesrvm.VM;
021    import org.jikesrvm.runtime.Statics;
022    import org.vmmagic.pragma.Uninterruptible;
023    import org.vmmagic.unboxed.Offset;
024    
025    /**
026     * Support code to parse a DataInputStream in the Java classfile format
027     * and create the appropriate instance of an RVMClass or UnboxedType.
028     * Also low-level support for our internal constant pool format.
029     */
030    public class ClassFileReader implements Constants, ClassLoaderConstants {
031    
032      /**
033       * Parse and return the constant pool in a class file
034       * @param typeRef the canonical type reference for this type.
035       * @param input the data stream from which to read the class's description.
036       */
037      static int[] readConstantPool(TypeReference typeRef, DataInputStream input)  throws ClassFormatError, IOException {
038    
039        int magic = input.readInt();
040        if (magic != 0xCAFEBABE) {
041          throw new ClassFormatError("bad magic number " + Integer.toHexString(magic));
042        }
043    
044        // Get the class file version number and check to see if it is a version
045        // that we support.
046        int minor = input.readUnsignedShort();
047        int major = input.readUnsignedShort();
048        switch (major) {
049          case 45:
050          case 46:
051          case 47:
052          case 48:
053          case 49: // we support all variants of these major versions so the minor number doesn't matter.
054            break;
055          case 50: // we only support up to 50.0 (ie Java 1.6.0)
056            if (minor == 0) break;
057          default:
058            throw new UnsupportedClassVersionError("unsupported class file version " + major + "." + minor);
059        }
060    
061        //
062        // pass 1: read constant pool
063        //
064        int[] constantPool = new int[input.readUnsignedShort()];
065        byte[] tmpTags = new byte[constantPool.length];
066    
067        // note: slot 0 is unused
068        for (int i = 1; i < constantPool.length; i++) {
069          tmpTags[i] = input.readByte();
070          switch (tmpTags[i]) {
071            case TAG_UTF: {
072              byte[] utf = new byte[input.readUnsignedShort()];
073              input.readFully(utf);
074              int atomId = Atom.findOrCreateUtf8Atom(utf).getId();
075              constantPool[i] = packCPEntry(CP_UTF, atomId);
076              break;
077            }
078            case TAG_UNUSED:
079              if (VM.VerifyAssertions) VM._assert(NOT_REACHED);
080              break;
081    
082            case TAG_INT: {
083              int literal = input.readInt();
084              int offset = Statics.findOrCreateIntSizeLiteral(literal);
085              constantPool[i] = packCPEntry(CP_INT, offset);
086              break;
087            }
088            case TAG_FLOAT: {
089              int literal = input.readInt();
090              int offset = Statics.findOrCreateIntSizeLiteral(literal);
091              constantPool[i] = packCPEntry(CP_FLOAT, offset);
092              break;
093            }
094            case TAG_LONG: {
095              long literal = input.readLong();
096              int offset = Statics.findOrCreateLongSizeLiteral(literal);
097              constantPool[i] = packCPEntry(CP_LONG, offset);
098              i++;
099              break;
100            }
101            case TAG_DOUBLE: {
102              long literal = input.readLong();
103              int offset = Statics.findOrCreateLongSizeLiteral(literal);
104              constantPool[i] = packCPEntry(CP_DOUBLE, offset);
105              i++;
106              break;
107            }
108            case TAG_TYPEREF:
109              constantPool[i] = input.readUnsignedShort();
110              break;
111    
112            case TAG_STRING:
113              constantPool[i] = input.readUnsignedShort();
114              break;
115    
116            case TAG_FIELDREF:
117            case TAG_METHODREF:
118            case TAG_INTERFACE_METHODREF: {
119              int classDescriptorIndex = input.readUnsignedShort();
120              int memberNameAndDescriptorIndex = input.readUnsignedShort();
121              constantPool[i] = packTempCPEntry(classDescriptorIndex, memberNameAndDescriptorIndex);
122              break;
123            }
124    
125            case TAG_MEMBERNAME_AND_DESCRIPTOR: {
126              int memberNameIndex = input.readUnsignedShort();
127              int descriptorIndex = input.readUnsignedShort();
128              constantPool[i] = packTempCPEntry(memberNameIndex, descriptorIndex);
129              break;
130            }
131    
132            default:
133              throw new ClassFormatError("bad constant pool");
134          }
135        }
136    
137        //
138        // pass 2: post-process type and string constant pool entries
139        // (we must do this in a second pass because of forward references)
140        //
141        try {
142          for (int i = 1; i < constantPool.length; i++) {
143            switch (tmpTags[i]) {
144              case TAG_LONG:
145              case TAG_DOUBLE:
146                ++i;
147                break;
148    
149              case TAG_TYPEREF: { // in: utf index
150                Atom typeName = getUtf(constantPool, constantPool[i]);
151                int typeRefId =
152                    TypeReference.findOrCreate(typeRef.getClassLoader(), typeName.descriptorFromClassName()).getId();
153                constantPool[i] = packCPEntry(CP_CLASS, typeRefId);
154                break;
155              } // out: type reference id
156    
157              case TAG_STRING: { // in: utf index
158                Atom literal = getUtf(constantPool, constantPool[i]);
159                int offset = literal.getStringLiteralOffset();
160                constantPool[i] = packCPEntry(CP_STRING, offset);
161                break;
162              } // out: jtoc slot number
163            }
164          }
165        } catch (java.io.UTFDataFormatException x) {
166          throw new ClassFormatError(x.toString());
167        }
168    
169        //
170        // pass 3: post-process type field and method constant pool entries
171        //
172        for (int i = 1; i < constantPool.length; i++) {
173          switch (tmpTags[i]) {
174            case TAG_LONG:
175            case TAG_DOUBLE:
176              ++i;
177              break;
178    
179            case TAG_FIELDREF:
180            case TAG_METHODREF:
181            case TAG_INTERFACE_METHODREF: { // in: classname+membername+memberdescriptor indices
182              int bits = constantPool[i];
183              int classNameIndex = unpackTempCPIndex1(bits);
184              int memberNameAndDescriptorIndex = unpackTempCPIndex2(bits);
185              int memberNameAndDescriptorBits = constantPool[memberNameAndDescriptorIndex];
186              int memberNameIndex = unpackTempCPIndex1(memberNameAndDescriptorBits);
187              int memberDescriptorIndex = unpackTempCPIndex2(memberNameAndDescriptorBits);
188    
189              TypeReference tref = getTypeRef(constantPool, classNameIndex);
190              Atom memberName = getUtf(constantPool, memberNameIndex);
191              Atom memberDescriptor = getUtf(constantPool, memberDescriptorIndex);
192              MemberReference mr = MemberReference.findOrCreate(tref, memberName, memberDescriptor);
193              int mrId = mr.getId();
194              constantPool[i] = packCPEntry(CP_MEMBER, mrId);
195              break;
196            } // out: MemberReference id
197          }
198        }
199        return constantPool;
200      }
201    
202      /**
203       * Read the class' TypeReference
204       * @param typeRef
205       * @param input
206       * @param constantPool
207       * @return the constantPool index of the typeRef of the class we are reading
208       */
209      static int readTypeRef(TypeReference typeRef, DataInputStream input, int[] constantPool) throws IOException, ClassFormatError {
210        int myTypeIndex = input.readUnsignedShort();
211        TypeReference myTypeRef = getTypeRef(constantPool, myTypeIndex);
212        if (myTypeRef != typeRef) {
213          // eg. file contains a different class than would be
214          // expected from its .class file name
215          if (!VM.VerifyAssertions) {
216            throw new ClassFormatError("expected class \"" +
217                                       typeRef.getName() +
218                                       "\" but found \"" +
219                                       myTypeRef.getName() +
220                                       "\"");
221          } else {
222            throw new ClassFormatError("expected class \"" +
223                                       typeRef.getName() +
224                                       "\" but found \"" +
225                                       myTypeRef.getName() +
226                                       "\"\n" + typeRef + " != " + myTypeRef);
227          }
228        }
229        return myTypeIndex;
230      }
231    
232      /**
233       * Read the super class name, load and resolve the super class
234       * @param input
235       * @param constantPool
236       * @param modifiers
237       * @return the super class of the class being read
238       */
239      static RVMClass readSuperClass(DataInputStream input, int[] constantPool,
240          short modifiers) throws IOException, NoClassDefFoundError {
241        TypeReference superType = getTypeRef(constantPool, input.readUnsignedShort()); // possibly null
242        RVMClass superClass = null;
243        if (((modifiers & ACC_INTERFACE) == 0) && (superType != null)) {
244          superClass = superType.resolve().asClass();
245        }
246        return superClass;
247      }
248    
249      /**
250       * Read the list of interfaces implemented by the class being read
251       * @param input
252       * @param constantPool
253       * @return the interfaces implemented by the class
254       */
255      static RVMClass[] readDeclaredInterfaces(DataInputStream input, int[] constantPool) throws IOException, NoClassDefFoundError {
256        int numInterfaces = input.readUnsignedShort();
257        RVMClass[] declaredInterfaces;
258        if (numInterfaces == 0) {
259          declaredInterfaces = RVMType.emptyVMClass;
260        } else {
261          declaredInterfaces = new RVMClass[numInterfaces];
262          for (int i = 0; i < numInterfaces; ++i) {
263            TypeReference inTR = getTypeRef(constantPool, input.readUnsignedShort());
264            declaredInterfaces[i] = inTR.resolve().asClass();
265          }
266        }
267        return declaredInterfaces;
268      }
269    
270      /**
271       * Read the declared fields of the class being read
272       * @param typeRef
273       * @param input
274       * @param constantPool
275       * @return the list of declared fields
276       */
277      static RVMField[] readDeclaredFields(TypeReference typeRef, DataInputStream input, int[] constantPool) throws IOException {
278        int numFields = input.readUnsignedShort();
279        RVMField[] declaredFields;
280        if (numFields == 0) {
281          declaredFields = RVMType.emptyVMField;
282        } else {
283          declaredFields = new RVMField[numFields];
284          for (int i = 0; i < numFields; i++) {
285            short fmodifiers = input.readShort();
286            Atom fieldName = getUtf(constantPool, input.readUnsignedShort());
287            Atom fieldDescriptor = getUtf(constantPool, input.readUnsignedShort());
288            if (typeRef == TypeReference.JavaLangSystem &&
289                (fmodifiers & (ACC_STATIC | ACC_FINAL | ACC_PUBLIC)) == (ACC_STATIC | ACC_FINAL | ACC_PUBLIC)) {
290              /* We have to stop System.in .out and .err fields from being final! */
291              fmodifiers -= ACC_FINAL;
292            }
293            MemberReference memRef = MemberReference.findOrCreate(typeRef, fieldName, fieldDescriptor);
294            declaredFields[i] = RVMField.readField(typeRef, constantPool, memRef, fmodifiers, input);
295          }
296        }
297        return declaredFields;
298      }
299    
300      /**
301       * Read the declared methods of the class being read
302       * @param typeRef
303       * @param input
304       * @param constantPool
305       * @return the declared methods of the class
306       */
307      static RVMMethod[] readDeclaredMethods(TypeReference typeRef, DataInputStream input, int[] constantPool) throws IOException {
308        int numMethods = input.readUnsignedShort();
309        RVMMethod[] declaredMethods;
310        if (numMethods == 0) {
311          declaredMethods = RVMType.emptyVMMethod;
312        } else {
313          declaredMethods = new RVMMethod[numMethods];
314          for (int i = 0; i < numMethods; i++) {
315            short mmodifiers = input.readShort();
316            Atom methodName = getUtf(constantPool, input.readUnsignedShort());
317            Atom methodDescriptor = getUtf(constantPool, input.readUnsignedShort());
318            MemberReference memRef = MemberReference.findOrCreate(typeRef, methodName, methodDescriptor);
319            RVMMethod method = RVMMethod.readMethod(typeRef, constantPool, memRef, mmodifiers, input);
320            declaredMethods[i] = method;
321          }
322        }
323        return declaredMethods;
324      }
325    
326      /**
327       * Return the class initializer method among the declared methods of the class
328       * @param declaredMethods
329       * @return the class initializer method <cinit> of the class
330       */
331      static RVMMethod getClassInitializerMethod(RVMMethod[] declaredMethods) {
332        for (RVMMethod method : declaredMethods) {
333          if (method.isClassInitializer()) return method;
334        }
335        return null;
336      }
337    
338      /**
339       * Create an instance of a RVMClass.
340       * @param typeRef the canonical type reference for this type.
341       * @param input the data stream from which to read the class's description.
342       */
343      static RVMClass readClass(TypeReference typeRef, DataInputStream input) throws ClassFormatError, IOException {
344    
345        if (RVMClass.classLoadingDisabled) {
346          throw new RuntimeException("ClassLoading Disabled : " + typeRef);
347        }
348    
349        if (VM.TraceClassLoading && VM.runningVM) {
350          VM.sysWrite("RVMClass: (begin) load file " + typeRef.getName() + "\n");
351        }
352    
353        int[] constantPool = readConstantPool(typeRef, input);
354        short modifiers = input.readShort();
355        int myTypeIndex = readTypeRef(typeRef, input, constantPool);
356        RVMClass superClass = readSuperClass(input, constantPool, modifiers);
357        RVMClass[] declaredInterfaces = readDeclaredInterfaces(input, constantPool);
358        RVMField[] declaredFields = readDeclaredFields(typeRef, input, constantPool);
359        RVMMethod[] declaredMethods = readDeclaredMethods(typeRef, input, constantPool);
360        RVMMethod classInitializerMethod = getClassInitializerMethod(declaredMethods);
361    
362        TypeReference[] declaredClasses = null;
363        Atom sourceName = null;
364        TypeReference declaringClass = null;
365        Atom signature = null;
366        RVMAnnotation[] annotations = null;
367        TypeReference enclosingClass = null;
368        MethodReference enclosingMethod = null;
369        // Read attributes.
370        for (int i = 0, n = input.readUnsignedShort(); i < n; ++i) {
371          Atom attName = getUtf(constantPool, input.readUnsignedShort());
372          int attLength = input.readInt();
373    
374          // Class attributes
375          if (attName == RVMClassLoader.sourceFileAttributeName && attLength == 2) {
376            sourceName = getUtf(constantPool, input.readUnsignedShort());
377          } else if (attName == RVMClassLoader.innerClassesAttributeName) {
378            // Parse InnerClasses attribute, and use the information to populate
379            // the list of declared member classes.  We do this so we can
380            // support the java.lang.Class.getDeclaredClasses()
381            // and java.lang.Class.getDeclaredClass methods.
382    
383            int numberOfClasses = input.readUnsignedShort();
384            declaredClasses = new TypeReference[numberOfClasses];
385    
386            for (int j = 0; j < numberOfClasses; ++j) {
387              int innerClassInfoIndex = input.readUnsignedShort();
388              int outerClassInfoIndex = input.readUnsignedShort();
389              int innerNameIndex = input.readUnsignedShort();
390              int innerClassAccessFlags = input.readUnsignedShort();
391    
392              if (innerClassInfoIndex != 0 && outerClassInfoIndex == myTypeIndex && innerNameIndex != 0) {
393                // This looks like a declared inner class.
394                declaredClasses[j] = getTypeRef(constantPool, innerClassInfoIndex);
395              }
396    
397              if (innerClassInfoIndex == myTypeIndex) {
398                if (outerClassInfoIndex != 0) {
399                  declaringClass = getTypeRef(constantPool, outerClassInfoIndex);
400                  if (enclosingClass == null) {
401                    // TODO: is this the null test necessary?
402                    enclosingClass = declaringClass;
403                  }
404                }
405                if ((innerClassAccessFlags & (ACC_PRIVATE | ACC_PROTECTED)) != 0) {
406                  modifiers &= ~(ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED);
407                }
408                modifiers |= innerClassAccessFlags;
409              }
410            }
411          } else if (attName == RVMClassLoader.syntheticAttributeName) {
412            modifiers |= ACC_SYNTHETIC;
413          } else if (attName == RVMClassLoader.enclosingMethodAttributeName) {
414            int enclosingClassIndex = input.readUnsignedShort();
415            enclosingClass = getTypeRef(constantPool, enclosingClassIndex);
416    
417            int enclosingMethodIndex = input.readUnsignedShort();
418            if (enclosingMethodIndex != 0) {
419              int memberNameIndex = constantPool[enclosingMethodIndex] >>> BITS_IN_SHORT;
420              int memberDescriptorIndex = constantPool[enclosingMethodIndex] & ((1 << BITS_IN_SHORT) - 1);
421              Atom memberName = getUtf(constantPool, memberNameIndex);
422              Atom memberDescriptor = getUtf(constantPool, memberDescriptorIndex);
423              enclosingMethod =
424                  MemberReference.findOrCreate(enclosingClass, memberName, memberDescriptor).asMethodReference();
425            }
426          } else if (attName == RVMClassLoader.signatureAttributeName) {
427            signature = getUtf(constantPool, input.readUnsignedShort());
428          } else if (attName == RVMClassLoader.runtimeVisibleAnnotationsAttributeName) {
429            annotations = AnnotatedElement.readAnnotations(constantPool, input, typeRef.getClassLoader());
430          } else {
431            int skippedAmount = input.skipBytes(attLength);
432            if (skippedAmount != attLength) {
433              throw new IOException("Unexpected short skip");
434            }
435          }
436        }
437    
438        return new RVMClass(typeRef,
439                            constantPool,
440                            modifiers,
441                            superClass,
442                            declaredInterfaces,
443                            declaredFields,
444                            declaredMethods,
445                            declaredClasses,
446                            declaringClass,
447                            enclosingClass,
448                            enclosingMethod,
449                            sourceName,
450                            classInitializerMethod,
451                            signature,
452                            annotations);
453      }
454    
455      @Uninterruptible
456      static int packCPEntry(byte type, int value) {
457        return (type << 29) | (value & 0x1fffffff);
458      }
459    
460      @Uninterruptible
461      static byte unpackCPType(int cpValue) {
462        return (byte) (cpValue >>> 29);
463      }
464    
465      @Uninterruptible
466      static int unpackSignedCPValue(int cpValue) {
467        return (cpValue << 3) >> 3;
468      }
469    
470      @Uninterruptible
471      static int unpackUnsignedCPValue(int cpValue) {
472        return cpValue & 0x1fffffff;
473      }
474    
475      @Uninterruptible
476      static boolean packedCPTypeIsClassType(int cpValue) {
477        return (cpValue & (7 << 29)) == (CP_CLASS << 29);
478      }
479    
480      @Uninterruptible
481      static int packTempCPEntry(int index1, int index2) {
482        return (index1 << 16) | (index2 & 0xffff);
483      }
484    
485      @Uninterruptible
486      static int unpackTempCPIndex1(int cpValue) {
487        return cpValue >>> 16;
488      }
489    
490      @Uninterruptible
491      static int unpackTempCPIndex2(int cpValue) {
492        return cpValue & 0xffff;
493      }
494    
495      static int getLiteralSize(int[] constantPool, int constantPoolIndex) {
496        int cpValue = constantPool[constantPoolIndex];
497        switch (unpackCPType(cpValue)) {
498          case CP_INT:
499          case CP_FLOAT:
500            return BYTES_IN_INT;
501          case CP_LONG:
502          case CP_DOUBLE:
503            return BYTES_IN_LONG;
504          case CP_CLASS:
505          case CP_STRING:
506            return BYTES_IN_ADDRESS;
507          default:
508            if (VM.VerifyAssertions) VM._assert(NOT_REACHED);
509            return 0;
510        }
511      }
512    
513      /**
514       * Get offset of a literal constant, in bytes.
515       * Offset is with respect to virtual machine's "table of contents" (jtoc).
516       */
517      static Offset getLiteralOffset(int[] constantPool, int constantPoolIndex) {
518        int cpValue = constantPool[constantPoolIndex];
519        if (VM.VerifyAssertions) {
520          int value = unpackSignedCPValue(cpValue);
521          byte type = unpackCPType(cpValue);
522          switch (type) {
523            case CP_INT:
524            case CP_FLOAT:
525            case CP_LONG:
526            case CP_DOUBLE:
527            case CP_STRING:
528              return Offset.fromIntSignExtend(value);
529            case CP_CLASS: {
530              int typeId = unpackUnsignedCPValue(cpValue);
531              Class<?> literalAsClass = TypeReference.getTypeRef(typeId).resolve().getClassForType();
532              return Offset.fromIntSignExtend(Statics.findOrCreateObjectLiteral(literalAsClass));
533            }
534            default:
535              VM._assert(NOT_REACHED);
536              return Offset.fromIntSignExtend(0xebad0ff5);
537          }
538        } else {
539          if (packedCPTypeIsClassType(cpValue)) {
540            int typeId = unpackUnsignedCPValue(cpValue);
541            Class<?> literalAsClass = TypeReference.getTypeRef(typeId).resolve().getClassForType();
542            return Offset.fromIntSignExtend(Statics.findOrCreateObjectLiteral(literalAsClass));
543          } else {
544            int value = unpackSignedCPValue(cpValue);
545            return Offset.fromIntSignExtend(value);
546          }
547        }
548      }
549    
550      /**
551       * Get description of a literal constant.
552       */
553      static byte getLiteralDescription(int[] constantPool, int constantPoolIndex) {
554        int cpValue = constantPool[constantPoolIndex];
555        byte type = unpackCPType(cpValue);
556        return type;
557      }
558    
559      /**
560       * Get contents of a "typeRef" constant pool entry.
561       * @return type that was referenced
562       */
563      @Uninterruptible
564      static TypeReference getTypeRef(int[] constantPool, int constantPoolIndex) {
565        if (constantPoolIndex != 0) {
566          int cpValue = constantPool[constantPoolIndex];
567          if (VM.VerifyAssertions) VM._assert(unpackCPType(cpValue) == CP_CLASS);
568          return TypeReference.getTypeRef(unpackUnsignedCPValue(cpValue));
569        } else {
570          return null;
571        }
572      }
573    
574      /**
575       * Get contents of a "methodRef" constant pool entry.
576       */
577      @Uninterruptible
578      static MethodReference getMethodRef(int[] constantPool, int constantPoolIndex) {
579        int cpValue = constantPool[constantPoolIndex];
580        if (VM.VerifyAssertions) VM._assert(unpackCPType(cpValue) == CP_MEMBER);
581        return (MethodReference) MemberReference.getMemberRef(unpackUnsignedCPValue(cpValue));
582      }
583    
584      /**
585       * Get contents of a "methodRef" constant pool entry.
586       */
587      @Uninterruptible
588      static FieldReference getFieldRef(int[] constantPool, int constantPoolIndex) {
589        int cpValue = constantPool[constantPoolIndex];
590        if (VM.VerifyAssertions) VM._assert(unpackCPType(cpValue) == CP_MEMBER);
591        return (FieldReference) MemberReference.getMemberRef(unpackUnsignedCPValue(cpValue));
592      }
593    
594      /**
595       * Get contents of a "utf" from a constant pool entry.
596       */
597      @Uninterruptible
598      static Atom getUtf(int[] constantPool, int constantPoolIndex) {
599        int cpValue = constantPool[constantPoolIndex];
600        if (VM.VerifyAssertions) VM._assert(unpackCPType(cpValue) == CP_UTF);
601        return Atom.getAtom(unpackUnsignedCPValue(cpValue));
602      }
603    }