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}