1 /* 2 * ProGuard -- shrinking, optimization, obfuscation, and preverification 3 * of Java bytecode. 4 * 5 * Copyright (c) 2002-2014 Eric Lafortune (eric@graphics.cornell.edu) 6 * 7 * This program is free software; you can redistribute it and/or modify it 8 * under the terms of the GNU General Public License as published by the Free 9 * Software Foundation; either version 2 of the License, or (at your option) 10 * any later version. 11 * 12 * This program is distributed in the hope that it will be useful, but WITHOUT 13 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 14 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 15 * more details. 16 * 17 * You should have received a copy of the GNU General Public License along 18 * with this program; if not, write to the Free Software Foundation, Inc., 19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 */ 21 package proguard.classfile.io; 22 23 import proguard.classfile.*; 24 import proguard.classfile.constant.*; 25 import proguard.classfile.constant.visitor.ConstantVisitor; 26 import proguard.classfile.util.*; 27 import proguard.classfile.visitor.*; 28 29 import java.io.DataInput; 30 31 /** 32 * This ClassVisitor fills out the LibraryClass objects that it visits with data 33 * from the given DataInput object. 34 * 35 * @author Eric Lafortune 36 */ 37 public class LibraryClassReader 38 extends SimplifiedVisitor 39 implements ClassVisitor, 40 MemberVisitor, 41 ConstantVisitor 42 { 43 private static final LibraryField[] EMPTY_LIBRARY_FIELDS = new LibraryField[0]; 44 private static final LibraryMethod[] EMPTY_LIBRARY_METHODS = new LibraryMethod[0]; 45 46 47 private final RuntimeDataInput dataInput; 48 private final boolean skipNonPublicClasses; 49 private final boolean skipNonPublicClassMembers; 50 51 // A global array that acts as a parameter for the visitor methods. 52 private Constant[] constantPool; 53 54 55 /** 56 * Creates a new ProgramClassReader for reading from the given DataInput. 57 */ LibraryClassReader(DataInput dataInput, boolean skipNonPublicClasses, boolean skipNonPublicClassMembers)58 public LibraryClassReader(DataInput dataInput, 59 boolean skipNonPublicClasses, 60 boolean skipNonPublicClassMembers) 61 { 62 this.dataInput = new RuntimeDataInput(dataInput); 63 this.skipNonPublicClasses = skipNonPublicClasses; 64 this.skipNonPublicClassMembers = skipNonPublicClassMembers; 65 } 66 67 68 // Implementations for ClassVisitor. 69 visitProgramClass(ProgramClass libraryClass)70 public void visitProgramClass(ProgramClass libraryClass) 71 { 72 } 73 74 visitLibraryClass(LibraryClass libraryClass)75 public void visitLibraryClass(LibraryClass libraryClass) 76 { 77 // Read and check the magic number. 78 int u4magic = dataInput.readInt(); 79 80 ClassUtil.checkMagicNumber(u4magic); 81 82 // Read and check the version numbers. 83 int u2minorVersion = dataInput.readUnsignedShort(); 84 int u2majorVersion = dataInput.readUnsignedShort(); 85 86 int u4version = ClassUtil.internalClassVersion(u2majorVersion, 87 u2minorVersion); 88 89 ClassUtil.checkVersionNumbers(u4version); 90 91 // Read the constant pool. Note that the first entry is not used. 92 int u2constantPoolCount = dataInput.readUnsignedShort(); 93 94 // Create the constant pool array. 95 constantPool = new Constant[u2constantPoolCount]; 96 97 for (int index = 1; index < u2constantPoolCount; index++) 98 { 99 Constant constant = createConstant(); 100 constant.accept(libraryClass, this); 101 102 int tag = constant.getTag(); 103 if (tag == ClassConstants.CONSTANT_Class || 104 tag == ClassConstants.CONSTANT_Utf8) 105 { 106 constantPool[index] = constant; 107 } 108 109 // Long constants and double constants take up two entries in the 110 // constant pool. 111 if (tag == ClassConstants.CONSTANT_Long || 112 tag == ClassConstants.CONSTANT_Double) 113 { 114 index++; 115 } 116 } 117 118 // Read the general class information. 119 libraryClass.u2accessFlags = dataInput.readUnsignedShort(); 120 121 // We may stop parsing this library class if it's not public anyway. 122 // E.g. only about 60% of all rt.jar classes need to be parsed. 123 if (skipNonPublicClasses && 124 AccessUtil.accessLevel(libraryClass.getAccessFlags()) < AccessUtil.PUBLIC) 125 { 126 return; 127 } 128 129 // Read the class and super class indices. 130 int u2thisClass = dataInput.readUnsignedShort(); 131 int u2superClass = dataInput.readUnsignedShort(); 132 133 // Store their actual names. 134 libraryClass.thisClassName = getClassName(u2thisClass); 135 libraryClass.superClassName = (u2superClass == 0) ? null : 136 getClassName(u2superClass); 137 138 // Read the interfaces 139 int u2interfacesCount = dataInput.readUnsignedShort(); 140 141 libraryClass.interfaceNames = new String[u2interfacesCount]; 142 for (int index = 0; index < u2interfacesCount; index++) 143 { 144 // Store the actual interface name. 145 int u2interface = dataInput.readUnsignedShort(); 146 libraryClass.interfaceNames[index] = getClassName(u2interface); 147 } 148 149 // Read the fields. 150 int u2fieldsCount = dataInput.readUnsignedShort(); 151 152 // Create the fields array. 153 LibraryField[] reusableFields = new LibraryField[u2fieldsCount]; 154 155 int visibleFieldsCount = 0; 156 for (int index = 0; index < u2fieldsCount; index++) 157 { 158 LibraryField field = new LibraryField(); 159 this.visitLibraryMember(libraryClass, field); 160 161 // Only store fields that are visible. 162 if (AccessUtil.accessLevel(field.getAccessFlags()) >= 163 (skipNonPublicClassMembers ? AccessUtil.PROTECTED : 164 AccessUtil.PACKAGE_VISIBLE)) 165 { 166 reusableFields[visibleFieldsCount++] = field; 167 } 168 } 169 170 // Copy the visible fields (if any) into a fields array of the right size. 171 if (visibleFieldsCount == 0) 172 { 173 libraryClass.fields = EMPTY_LIBRARY_FIELDS; 174 } 175 else 176 { 177 libraryClass.fields = new LibraryField[visibleFieldsCount]; 178 System.arraycopy(reusableFields, 0, libraryClass.fields, 0, visibleFieldsCount); 179 } 180 181 // Read the methods. 182 int u2methodsCount = dataInput.readUnsignedShort(); 183 184 // Create the methods array. 185 LibraryMethod[] reusableMethods = new LibraryMethod[u2methodsCount]; 186 187 int visibleMethodsCount = 0; 188 for (int index = 0; index < u2methodsCount; index++) 189 { 190 LibraryMethod method = new LibraryMethod(); 191 this.visitLibraryMember(libraryClass, method); 192 193 // Only store methods that are visible. 194 if (AccessUtil.accessLevel(method.getAccessFlags()) >= 195 (skipNonPublicClassMembers ? AccessUtil.PROTECTED : 196 AccessUtil.PACKAGE_VISIBLE)) 197 { 198 reusableMethods[visibleMethodsCount++] = method; 199 } 200 } 201 202 // Copy the visible methods (if any) into a methods array of the right size. 203 if (visibleMethodsCount == 0) 204 { 205 libraryClass.methods = EMPTY_LIBRARY_METHODS; 206 } 207 else 208 { 209 libraryClass.methods = new LibraryMethod[visibleMethodsCount]; 210 System.arraycopy(reusableMethods, 0, libraryClass.methods, 0, visibleMethodsCount); 211 } 212 213 // Skip the class attributes. 214 skipAttributes(); 215 } 216 217 218 // Implementations for MemberVisitor. 219 visitProgramMember(ProgramClass libraryClass, ProgramMember libraryMember)220 public void visitProgramMember(ProgramClass libraryClass, ProgramMember libraryMember) 221 { 222 } 223 224 visitLibraryMember(LibraryClass libraryClass, LibraryMember libraryMember)225 public void visitLibraryMember(LibraryClass libraryClass, LibraryMember libraryMember) 226 { 227 // Read the general field information. 228 libraryMember.u2accessFlags = dataInput.readUnsignedShort(); 229 libraryMember.name = getString(dataInput.readUnsignedShort()); 230 libraryMember.descriptor = getString(dataInput.readUnsignedShort()); 231 232 // Skip the field attributes. 233 skipAttributes(); 234 } 235 236 237 // Implementations for ConstantVisitor. 238 visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant)239 public void visitIntegerConstant(Clazz clazz, IntegerConstant integerConstant) 240 { 241 dataInput.skipBytes(4); 242 } 243 244 visitLongConstant(Clazz clazz, LongConstant longConstant)245 public void visitLongConstant(Clazz clazz, LongConstant longConstant) 246 { 247 dataInput.skipBytes(8); 248 } 249 250 visitFloatConstant(Clazz clazz, FloatConstant floatConstant)251 public void visitFloatConstant(Clazz clazz, FloatConstant floatConstant) 252 { 253 dataInput.skipBytes(4); 254 } 255 256 visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant)257 public void visitDoubleConstant(Clazz clazz, DoubleConstant doubleConstant) 258 { 259 dataInput.skipBytes(8); 260 } 261 262 visitStringConstant(Clazz clazz, StringConstant stringConstant)263 public void visitStringConstant(Clazz clazz, StringConstant stringConstant) 264 { 265 dataInput.skipBytes(2); 266 } 267 268 visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant)269 public void visitUtf8Constant(Clazz clazz, Utf8Constant utf8Constant) 270 { 271 int u2length = dataInput.readUnsignedShort(); 272 273 // Read the UTF-8 bytes. 274 byte[] bytes = new byte[u2length]; 275 dataInput.readFully(bytes); 276 utf8Constant.setBytes(bytes); 277 } 278 279 visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant)280 public void visitInvokeDynamicConstant(Clazz clazz, InvokeDynamicConstant invokeDynamicConstant) 281 { 282 dataInput.skipBytes(4); 283 } 284 285 visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant)286 public void visitMethodHandleConstant(Clazz clazz, MethodHandleConstant methodHandleConstant) 287 { 288 dataInput.skipBytes(3); 289 } 290 291 visitAnyRefConstant(Clazz clazz, RefConstant refConstant)292 public void visitAnyRefConstant(Clazz clazz, RefConstant refConstant) 293 { 294 dataInput.skipBytes(4); 295 } 296 297 visitClassConstant(Clazz clazz, ClassConstant classConstant)298 public void visitClassConstant(Clazz clazz, ClassConstant classConstant) 299 { 300 classConstant.u2nameIndex = dataInput.readUnsignedShort(); 301 } 302 303 visitMethodTypeConstant(Clazz clazz, MethodTypeConstant methodTypeConstant)304 public void visitMethodTypeConstant(Clazz clazz, MethodTypeConstant methodTypeConstant) 305 { 306 dataInput.skipBytes(2); 307 } 308 309 visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant)310 public void visitNameAndTypeConstant(Clazz clazz, NameAndTypeConstant nameAndTypeConstant) 311 { 312 dataInput.skipBytes(4); 313 } 314 315 316 // Small utility methods. 317 318 /** 319 * Returns the class name of the ClassConstant at the specified index in the 320 * reusable constant pool. 321 */ getClassName(int constantIndex)322 private String getClassName(int constantIndex) 323 { 324 ClassConstant classEntry = (ClassConstant)constantPool[constantIndex]; 325 326 return getString(classEntry.u2nameIndex); 327 } 328 329 330 /** 331 * Returns the string of the Utf8Constant at the specified index in the 332 * reusable constant pool. 333 */ getString(int constantIndex)334 private String getString(int constantIndex) 335 { 336 return ((Utf8Constant)constantPool[constantIndex]).getString(); 337 } 338 339 createConstant()340 private Constant createConstant() 341 { 342 int u1tag = dataInput.readUnsignedByte(); 343 344 switch (u1tag) 345 { 346 case ClassConstants.CONSTANT_Integer: return new IntegerConstant(); 347 case ClassConstants.CONSTANT_Float: return new FloatConstant(); 348 case ClassConstants.CONSTANT_Long: return new LongConstant(); 349 case ClassConstants.CONSTANT_Double: return new DoubleConstant(); 350 case ClassConstants.CONSTANT_String: return new StringConstant(); 351 case ClassConstants.CONSTANT_Utf8: return new Utf8Constant(); 352 case ClassConstants.CONSTANT_InvokeDynamic: return new InvokeDynamicConstant(); 353 case ClassConstants.CONSTANT_MethodHandle: return new MethodHandleConstant(); 354 case ClassConstants.CONSTANT_Fieldref: return new FieldrefConstant(); 355 case ClassConstants.CONSTANT_Methodref: return new MethodrefConstant(); 356 case ClassConstants.CONSTANT_InterfaceMethodref: return new InterfaceMethodrefConstant(); 357 case ClassConstants.CONSTANT_Class: return new ClassConstant(); 358 case ClassConstants.CONSTANT_MethodType: return new MethodTypeConstant(); 359 case ClassConstants.CONSTANT_NameAndType: return new NameAndTypeConstant(); 360 361 default: throw new RuntimeException("Unknown constant type ["+u1tag+"] in constant pool"); 362 } 363 } 364 365 skipAttributes()366 private void skipAttributes() 367 { 368 int u2attributesCount = dataInput.readUnsignedShort(); 369 370 for (int index = 0; index < u2attributesCount; index++) 371 { 372 skipAttribute(); 373 } 374 } 375 376 skipAttribute()377 private void skipAttribute() 378 { 379 dataInput.skipBytes(2); 380 int u4attributeLength = dataInput.readInt(); 381 dataInput.skipBytes(u4attributeLength); 382 } 383 } 384