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