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 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 }