1 /* 2 * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 * 23 */ 24 25 package gc.stress.gcbasher; 26 27 class Decompiler { 28 private ByteCursor cursor; 29 private ClassInfo ci; 30 31 public Decompiler(byte[] classData) { 32 cursor = new ByteCursor(classData); 33 34 int magicNumber = cursor.readInt(); 35 if (magicNumber != 0xCAFEBABE) { 36 throw new IllegalArgumentException("Bad magic number " + magicNumber); 37 } 38 39 cursor.readUnsignedShort(); // Minor version 40 cursor.readUnsignedShort(); // Major version 41 42 ConstantPoolEntry[] constantPool = decodeConstantPool(); 43 44 cursor.readUnsignedShort(); // Access flags 45 46 // this class index in constant pool; 47 int classInfo = cursor.readUnsignedShort(); 48 int classInfoNameIndex = constantPool[classInfo].getNameIndex(); 49 ci = new ClassInfo(constantPool[classInfoNameIndex].getValue()); 50 51 cursor.readUnsignedShort(); // superclass 52 53 int numInterfaces = cursor.readUnsignedShort(); 54 for (int i = 0; i < numInterfaces; i++) { 55 cursor.readUnsignedShort(); // interface 56 } 57 58 decodeFields(); 59 MethodInfo[] methods = decodeMethods(constantPool); 60 decodeMethodDependencies(methods, constantPool); 61 } 62 63 public ClassInfo getClassInfo() { 64 return ci; 65 } 66 67 private boolean isDependency(String name, String className) { 68 return !name.equals(className) && !name.startsWith("["); 69 } 70 71 private void addDependency(MethodInfo m, String name) { 72 Dependency d = new Dependency(m.getName(), m.getDescriptor(), name); 73 ci.addResolutionDep(d); 74 } 75 76 private String resolveName(ConstantPoolEntry[] constantPool, int cpi) { 77 int nameIndex = constantPool[cpi].getNameIndex(); 78 return constantPool[nameIndex].getValue(); 79 } 80 81 private void decodeMethodDependencies(MethodInfo[] methods, ConstantPoolEntry[] constantPool) { 82 for (int i = 0; i < methods.length; i++) { 83 MethodInfo m = methods[i]; 84 final int stopCheck = m.getCodeStart() + m.getCodeLength(); 85 86 int byteCodeIndex = m.getCodeStart(); 87 while (byteCodeIndex < stopCheck) { 88 int bc = cursor.readUnsignedByteAt(byteCodeIndex); 89 90 switch (bc) { 91 // These opcodes cause name resolution or initialization 92 // Their index bytes all point to a CONSTANT_Class (4.4.1) 93 case Bytecode.ANEWARRAY: 94 case Bytecode.CHECKCAST: 95 case Bytecode.INSTANCEOF: 96 case Bytecode.MULTIANEWARRAY: 97 case Bytecode.NEW: { 98 int cpi = cursor.readUnsignedShortAt(byteCodeIndex + 1); 99 String name = resolveName(constantPool, cpi); 100 101 if (isDependency(name, ci.getName())) { 102 addDependency(m, name); 103 } 104 break; 105 } 106 107 // These opcodes cause name resolution or initialization 108 // Their index bytes all point to a CONSTANT_Field/Methodref (4.4.2) 109 case Bytecode.GETFIELD: 110 case Bytecode.INVOKEINTERFACE: 111 case Bytecode.INVOKESPECIAL: 112 case Bytecode.INVOKEVIRTUAL: 113 case Bytecode.PUTFIELD: 114 case Bytecode.PUTSTATIC: 115 case Bytecode.GETSTATIC: 116 case Bytecode.INVOKESTATIC: { 117 int cpi = cursor.readUnsignedShortAt(byteCodeIndex + 1); 118 int classIndex = constantPool[cpi].getClassIndex(); 119 String name = resolveName(constantPool, classIndex); 120 121 if (isDependency(name, ci.getName())) { 122 addDependency(m, name); 123 } 124 break; 125 } 126 127 case Bytecode.LOOKUPSWITCH: { 128 byteCodeIndex++; 129 int offset = byteCodeIndex - m.getCodeStart(); 130 while (offset % 4 != 0) { 131 offset++; 132 byteCodeIndex++; 133 } 134 135 int def = cursor.readIntAt(byteCodeIndex); 136 byteCodeIndex +=4; 137 138 int npairs = cursor.readIntAt(byteCodeIndex); 139 byteCodeIndex +=4; 140 byteCodeIndex += (8 * npairs); 141 continue; 142 } 143 144 case Bytecode.TABLESWITCH: { 145 byteCodeIndex++; 146 int offset = byteCodeIndex - m.getCodeStart(); 147 while (offset % 4 != 0) { 148 offset++; 149 byteCodeIndex++; 150 } 151 152 int def = cursor.readIntAt(byteCodeIndex); 153 byteCodeIndex +=4; 154 155 int low = cursor.readIntAt(byteCodeIndex); 156 byteCodeIndex +=4; 157 int high = cursor.readIntAt(byteCodeIndex); 158 byteCodeIndex +=4; 159 byteCodeIndex += (4 * (high - low + 1)); 160 continue; 161 } 162 163 case Bytecode.WIDE: { 164 bc = cursor.readUnsignedByteAt(++byteCodeIndex); 165 if (bc == Bytecode.IINC) { 166 byteCodeIndex += 5; 167 } else { 168 byteCodeIndex += 3; 169 } 170 continue; 171 } 172 } 173 174 byteCodeIndex += Bytecode.getLength(bc); 175 } 176 177 if (byteCodeIndex - stopCheck > 1) { 178 String err = "bad finish for method " + m.getName() + 179 "End + " + (byteCodeIndex - stopCheck); 180 throw new IllegalArgumentException(err); 181 } 182 } 183 } 184 185 private MethodInfo[] decodeMethods(ConstantPoolEntry[] constantPool) { 186 MethodInfo[] methods = new MethodInfo[cursor.readUnsignedShort()]; 187 188 for (int i = 0; i < methods.length; i++) { 189 cursor.readUnsignedShort(); // access flags 190 191 String name = constantPool[cursor.readUnsignedShort()].getValue(); 192 String descriptor = constantPool[cursor.readUnsignedShort()].getValue(); 193 194 int codeLength = 0; 195 int codeStart = 0; 196 197 int numAttributes = cursor.readUnsignedShort(); // attributes count 198 for (int j = 0; j < numAttributes; j++) { 199 int type = cursor.readUnsignedShort(); // attrib nameIndex 200 int aLen = cursor.readInt(); // attrib length 201 202 if (constantPool[type].getValue().equals("Code")) { 203 cursor.readUnsignedShort(); // Max stack 204 cursor.readUnsignedShort(); // Max locals 205 206 codeLength = cursor.readInt(); 207 codeStart = cursor.getOffset(); 208 209 cursor.skipBytes(codeLength); // Need to skip the code bytes 210 cursor.skipBytes(cursor.readUnsignedShort() * 8); // Skip exception table 211 212 int numSubAttributes = cursor.readUnsignedShort(); 213 for (int k = 0; k < numSubAttributes; k++) { 214 cursor.readUnsignedShort(); // sub name 215 cursor.skipBytes(cursor.readInt()); // sub attrib data 216 } 217 } else { 218 cursor.skipBytes(aLen); // unknown attrib data 219 } 220 } 221 222 methods[i] = new MethodInfo(name, descriptor, codeLength, codeStart); 223 } 224 225 return methods; 226 } 227 228 private void decodeFields() { 229 // Looks like we dont need any field info, throw it away! 230 int numFields = cursor.readUnsignedShort(); 231 232 for (int i = 0; i < numFields; i++) { 233 cursor.readUnsignedShort(); // access flags 234 cursor.readUnsignedShort(); // nameIndex 235 cursor.readUnsignedShort(); // descriptorIndex 236 237 int numAttributes = cursor.readUnsignedShort(); 238 for (int j = 0; j < numAttributes; j++) { 239 cursor.readUnsignedShort(); // nameIndex 240 int length = cursor.readInt(); 241 cursor.skipBytes(length); // data 242 } 243 } 244 } 245 246 private ConstantPoolEntry[] decodeConstantPool() { 247 final int CONSTANT_Utf8 = 1; 248 final int CONSTANT_Unicode = 2; 249 final int CONSTANT_Integer = 3; 250 final int CONSTANT_Float = 4; 251 final int CONSTANT_Long = 5; 252 final int CONSTANT_Double = 6; 253 final int CONSTANT_Class = 7; 254 final int CONSTANT_String = 8; 255 final int CONSTANT_Fieldref = 9; 256 final int CONSTANT_Methodref = 10; 257 final int CONSTANT_InterfaceMethodref = 11; 258 final int CONSTANT_NameAndType = 12; 259 final int CONSTANT_MethodHandle = 15; 260 final int CONSTANT_MethodType = 16; 261 final int CONSTANT_InvokeDynamic = 18; 262 263 ConstantPoolEntry[] constantPool = new ConstantPoolEntry[cursor.readUnsignedShort()]; 264 265 // The constant pool starts at index 1 266 for (int i = 1; i < constantPool.length; i++) { 267 int type = cursor.readUnsignedByte(); 268 269 switch (type) { 270 case CONSTANT_Class: 271 constantPool[i] = new ConstantPoolEntry(cursor.readUnsignedShort()); // name_index 272 break; 273 274 case CONSTANT_Fieldref: case CONSTANT_Methodref: case CONSTANT_InterfaceMethodref: 275 constantPool[i] = new ConstantPoolEntry(cursor.readUnsignedShort()); // class_index 276 cursor.readUnsignedShort(); // name_and_type_index 277 break; 278 279 case CONSTANT_String: 280 cursor.readUnsignedShort(); // string_index 281 break; 282 283 case CONSTANT_Integer: 284 cursor.readInt(); // bytes 285 break; 286 287 case CONSTANT_Float: 288 cursor.readInt(); // bytes 289 break; 290 291 case CONSTANT_Long: 292 cursor.readInt(); // high_bytes 293 cursor.readInt(); // low_bytes 294 i++; // 8 byte constants use 2 constant pool slots. 295 break; 296 297 case CONSTANT_Double: 298 cursor.readInt(); // high_bytes 299 cursor.readInt(); // low_bytes 300 i++; // 8 byte constants use 2 constant pool slots. 301 break; 302 303 case CONSTANT_NameAndType: 304 constantPool[i] = new ConstantPoolEntry(cursor.readUnsignedShort()); // name_index 305 cursor.readUnsignedShort(); // descriptor_index 306 break; 307 308 case CONSTANT_Utf8: 309 int length = cursor.readUnsignedShort(); // length 310 constantPool[i] = new ConstantPoolEntry(cursor.readUtf8(length)); // bytes[length] 311 break; 312 313 case CONSTANT_MethodHandle: 314 cursor.readUnsignedByte(); // reference_kind 315 cursor.readUnsignedShort(); // reference_index 316 break; 317 318 case CONSTANT_MethodType: 319 cursor.readUnsignedShort(); // descriptor_index 320 break; 321 322 case CONSTANT_InvokeDynamic: 323 cursor.readUnsignedShort(); // bootstrap_method_attr_index 324 cursor.readUnsignedShort(); // name_and_type_index 325 break; 326 327 default: 328 String err = "Unknown constant pool type " + String.valueOf(type) + "\n" + 329 "CPE " + i + " of " + constantPool.length + "\n" + 330 "Byte offset " + Integer.toHexString(cursor.getOffset()); 331 throw new IllegalArgumentException(err); 332 } 333 } 334 return constantPool; 335 } 336 }