/* * Copyright (c) 2016, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. * */ package gc.stress.gcbasher; class Decompiler { private ByteCursor cursor; private ClassInfo ci; public Decompiler(byte[] classData) { cursor = new ByteCursor(classData); int magicNumber = cursor.readInt(); if (magicNumber != 0xCAFEBABE) { throw new IllegalArgumentException("Bad magic number " + magicNumber); } cursor.readUnsignedShort(); // Minor version cursor.readUnsignedShort(); // Major version ConstantPoolEntry[] constantPool = decodeConstantPool(); cursor.readUnsignedShort(); // Access flags // this class index in constant pool; int classInfo = cursor.readUnsignedShort(); int classInfoNameIndex = constantPool[classInfo].getNameIndex(); ci = new ClassInfo(constantPool[classInfoNameIndex].getValue()); cursor.readUnsignedShort(); // superclass int numInterfaces = cursor.readUnsignedShort(); for (int i = 0; i < numInterfaces; i++) { cursor.readUnsignedShort(); // interface } decodeFields(); MethodInfo[] methods = decodeMethods(constantPool); decodeMethodDependencies(methods, constantPool); } public ClassInfo getClassInfo() { return ci; } private boolean isDependency(String name, String className) { return !name.equals(className) && !name.startsWith("["); } private void addDependency(MethodInfo m, String name) { Dependency d = new Dependency(m.getName(), m.getDescriptor(), name); ci.addResolutionDep(d); } private String resolveName(ConstantPoolEntry[] constantPool, int cpi) { int nameIndex = constantPool[cpi].getNameIndex(); return constantPool[nameIndex].getValue(); } private void decodeMethodDependencies(MethodInfo[] methods, ConstantPoolEntry[] constantPool) { for (int i = 0; i < methods.length; i++) { MethodInfo m = methods[i]; final int stopCheck = m.getCodeStart() + m.getCodeLength(); int byteCodeIndex = m.getCodeStart(); while (byteCodeIndex < stopCheck) { int bc = cursor.readUnsignedByteAt(byteCodeIndex); switch (bc) { // These opcodes cause name resolution or initialization // Their index bytes all point to a CONSTANT_Class (4.4.1) case Bytecode.ANEWARRAY: case Bytecode.CHECKCAST: case Bytecode.INSTANCEOF: case Bytecode.MULTIANEWARRAY: case Bytecode.NEW: { int cpi = cursor.readUnsignedShortAt(byteCodeIndex + 1); String name = resolveName(constantPool, cpi); if (isDependency(name, ci.getName())) { addDependency(m, name); } break; } // These opcodes cause name resolution or initialization // Their index bytes all point to a CONSTANT_Field/Methodref (4.4.2) case Bytecode.GETFIELD: case Bytecode.INVOKEINTERFACE: case Bytecode.INVOKESPECIAL: case Bytecode.INVOKEVIRTUAL: case Bytecode.PUTFIELD: case Bytecode.PUTSTATIC: case Bytecode.GETSTATIC: case Bytecode.INVOKESTATIC: { int cpi = cursor.readUnsignedShortAt(byteCodeIndex + 1); int classIndex = constantPool[cpi].getClassIndex(); String name = resolveName(constantPool, classIndex); if (isDependency(name, ci.getName())) { addDependency(m, name); } break; } case Bytecode.LOOKUPSWITCH: { byteCodeIndex++; int offset = byteCodeIndex - m.getCodeStart(); while (offset % 4 != 0) { offset++; byteCodeIndex++; } cursor.readIntAt(byteCodeIndex); // def byteCodeIndex +=4; int npairs = cursor.readIntAt(byteCodeIndex); byteCodeIndex +=4; byteCodeIndex += (8 * npairs); continue; } case Bytecode.TABLESWITCH: { byteCodeIndex++; int offset = byteCodeIndex - m.getCodeStart(); while (offset % 4 != 0) { offset++; byteCodeIndex++; } cursor.readIntAt(byteCodeIndex); // def byteCodeIndex +=4; int low = cursor.readIntAt(byteCodeIndex); byteCodeIndex +=4; int high = cursor.readIntAt(byteCodeIndex); byteCodeIndex +=4; byteCodeIndex += (4 * (high - low + 1)); continue; } case Bytecode.WIDE: { bc = cursor.readUnsignedByteAt(++byteCodeIndex); if (bc == Bytecode.IINC) { byteCodeIndex += 5; } else { byteCodeIndex += 3; } continue; } } byteCodeIndex += Bytecode.getLength(bc); } if (byteCodeIndex - stopCheck > 1) { String err = "bad finish for method " + m.getName() + "End + " + (byteCodeIndex - stopCheck); throw new IllegalArgumentException(err); } } } private MethodInfo[] decodeMethods(ConstantPoolEntry[] constantPool) { MethodInfo[] methods = new MethodInfo[cursor.readUnsignedShort()]; for (int i = 0; i < methods.length; i++) { cursor.readUnsignedShort(); // access flags String name = constantPool[cursor.readUnsignedShort()].getValue(); String descriptor = constantPool[cursor.readUnsignedShort()].getValue(); int codeLength = 0; int codeStart = 0; int numAttributes = cursor.readUnsignedShort(); // attributes count for (int j = 0; j < numAttributes; j++) { int type = cursor.readUnsignedShort(); // attrib nameIndex int aLen = cursor.readInt(); // attrib length if (constantPool[type].getValue().equals("Code")) { cursor.readUnsignedShort(); // Max stack cursor.readUnsignedShort(); // Max locals codeLength = cursor.readInt(); codeStart = cursor.getOffset(); cursor.skipBytes(codeLength); // Need to skip the code bytes cursor.skipBytes(cursor.readUnsignedShort() * 8); // Skip exception table int numSubAttributes = cursor.readUnsignedShort(); for (int k = 0; k < numSubAttributes; k++) { cursor.readUnsignedShort(); // sub name cursor.skipBytes(cursor.readInt()); // sub attrib data } } else { cursor.skipBytes(aLen); // unknown attrib data } } methods[i] = new MethodInfo(name, descriptor, codeLength, codeStart); } return methods; } private void decodeFields() { // Looks like we dont need any field info, throw it away! int numFields = cursor.readUnsignedShort(); for (int i = 0; i < numFields; i++) { cursor.readUnsignedShort(); // access flags cursor.readUnsignedShort(); // nameIndex cursor.readUnsignedShort(); // descriptorIndex int numAttributes = cursor.readUnsignedShort(); for (int j = 0; j < numAttributes; j++) { cursor.readUnsignedShort(); // nameIndex int length = cursor.readInt(); cursor.skipBytes(length); // data } } } private ConstantPoolEntry[] decodeConstantPool() { final int CONSTANT_Utf8 = 1; // final int CONSTANT_Unicode = 2; final int CONSTANT_Integer = 3; final int CONSTANT_Float = 4; final int CONSTANT_Long = 5; final int CONSTANT_Double = 6; final int CONSTANT_Class = 7; final int CONSTANT_String = 8; final int CONSTANT_Fieldref = 9; final int CONSTANT_Methodref = 10; final int CONSTANT_InterfaceMethodref = 11; final int CONSTANT_NameAndType = 12; final int CONSTANT_MethodHandle = 15; final int CONSTANT_MethodType = 16; final int CONSTANT_InvokeDynamic = 18; ConstantPoolEntry[] constantPool = new ConstantPoolEntry[cursor.readUnsignedShort()]; // The constant pool starts at index 1 for (int i = 1; i < constantPool.length; i++) { int type = cursor.readUnsignedByte(); switch (type) { case CONSTANT_Class: constantPool[i] = new ConstantPoolEntry(cursor.readUnsignedShort()); // name_index break; case CONSTANT_Fieldref: case CONSTANT_Methodref: case CONSTANT_InterfaceMethodref: constantPool[i] = new ConstantPoolEntry(cursor.readUnsignedShort()); // class_index cursor.readUnsignedShort(); // name_and_type_index break; case CONSTANT_String: cursor.readUnsignedShort(); // string_index break; case CONSTANT_Integer: cursor.readInt(); // bytes break; case CONSTANT_Float: cursor.readInt(); // bytes break; case CONSTANT_Long: cursor.readInt(); // high_bytes cursor.readInt(); // low_bytes i++; // 8 byte constants use 2 constant pool slots. break; case CONSTANT_Double: cursor.readInt(); // high_bytes cursor.readInt(); // low_bytes i++; // 8 byte constants use 2 constant pool slots. break; case CONSTANT_NameAndType: constantPool[i] = new ConstantPoolEntry(cursor.readUnsignedShort()); // name_index cursor.readUnsignedShort(); // descriptor_index break; case CONSTANT_Utf8: int length = cursor.readUnsignedShort(); // length constantPool[i] = new ConstantPoolEntry(cursor.readUtf8(length)); // bytes[length] break; case CONSTANT_MethodHandle: cursor.readUnsignedByte(); // reference_kind cursor.readUnsignedShort(); // reference_index break; case CONSTANT_MethodType: cursor.readUnsignedShort(); // descriptor_index break; case CONSTANT_InvokeDynamic: cursor.readUnsignedShort(); // bootstrap_method_attr_index cursor.readUnsignedShort(); // name_and_type_index break; default: String err = "Unknown constant pool type " + String.valueOf(type) + "\n" + "CPE " + i + " of " + constantPool.length + "\n" + "Byte offset " + Integer.toHexString(cursor.getOffset()); throw new IllegalArgumentException(err); } } return constantPool; } }