All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.bytecode.ClassReader Maven / Gradle / Ivy

There is a newer version: 3.2.1
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.apache.bytecode;


import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;


/**
 * This is the class file reader for obtaining the parameter names
 * for declared methods in a class.  The class must have debugging
 * attributes for us to obtain this information. 

*

* This does not work for inherited methods. To obtain parameter * names for inherited methods, you must use a paramReader for the * class that originally declared the method.

*

* don't get tricky, it's the bare minimum. Instances of this class * are not threadsafe -- don't share them.

*/ public class ClassReader extends ByteArrayInputStream { // constants values that appear in java class files, // from jvm spec 2nd ed, section 4.4, pp 103 private static final int CONSTANT_Class = 7; private static final int CONSTANT_Fieldref = 9; private static final int CONSTANT_Methodref = 10; private static final int CONSTANT_InterfaceMethodref = 11; private static final int CONSTANT_String = 8; private static final int CONSTANT_Integer = 3; private static final int CONSTANT_Float = 4; private static final int CONSTANT_Long = 5; private static final int CONSTANT_Double = 6; private static final int CONSTANT_NameAndType = 12; private static final int CONSTANT_Utf8 = 1; /*java 8 9 10 11 new tokens https://docs.oracle.com/javase/specs/jvms/se11/html/jvms-4.html*/ private static final int CONSTANT_MethodHandle = 15; private static final int CONSTANT_MethodType = 16; private static final int CONSTANT_Dynamic = 17; private static final int CONSTANT_InvokeDynamic = 18; private static final int CONSTANT_Module = 19; private static final int CONSTANT_Package = 20; /*end of ava 8 9 10 11 new tokens*/ /** * the constant pool. constant pool indices in the class file * directly index into this array. The value stored in this array * is the position in the class file where that constant begins. */ private int[] cpoolIndex; private Object[] cpool; private Map attrMethods; /** * Loads the bytecode for a given class, by using the class's defining * classloader and assuming that for a class named P.C, the bytecodes are * in a resource named /P/C.class. * * @param c the class of interest * @return Returns a byte array containing the bytecode * @throws IOException */ protected static byte[] getBytes(Class c) throws IOException { InputStream fin = c.getResourceAsStream('/' + c.getName().replace('.', '/') + ".class"); if (fin == null) { throw new IOException("Unable to load bytecode for class " + c.getName() ); } try { ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] buf = new byte[1024]; int actual; do { actual = fin.read(buf); if (actual > 0) { out.write(buf, 0, actual); } } while (actual > 0); return out.toByteArray(); } finally { fin.close(); } } static String classDescriptorToName(String desc) { return desc.replace('/', '.'); } protected static Map findAttributeReaders(Class c) { HashMap map = new HashMap(); Method[] methods = c.getMethods(); for (int i = 0; i < methods.length; i++) { String name = methods[i].getName(); if (name.startsWith("read") && methods[i].getReturnType() == void.class) { map.put(name.substring(4), methods[i]); } } return map; } protected static String getSignature(Member method, Class[] paramTypes) { // compute the method descriptor StringBuffer b = new StringBuffer((method instanceof Method) ? method.getName() : ""); b.append('('); for (int i = 0; i < paramTypes.length; i++) { addDescriptor(b, paramTypes[i]); } b.append(')'); if (method instanceof Method) { addDescriptor(b, ((Method) method).getReturnType()); } else if (method instanceof Constructor) { addDescriptor(b, void.class); } return b.toString(); } private static void addDescriptor(StringBuffer b, Class c) { if (c.isPrimitive()) { if (c == void.class) b.append('V'); else if (c == int.class) b.append('I'); else if (c == boolean.class) b.append('Z'); else if (c == byte.class) b.append('B'); else if (c == short.class) b.append('S'); else if (c == long.class) b.append('J'); else if (c == char.class) b.append('C'); else if (c == float.class) b.append('F'); else if (c == double.class) b.append('D'); } else if (c.isArray()) { b.append('['); addDescriptor(b, c.getComponentType()); } else { b.append('L').append(c.getName().replace('.', '/')).append(';'); } } /** * @return Returns the next unsigned 16 bit value. */ protected final int readShort() { return (read() << 8) | read(); } /** * @return Returns the next signed 32 bit value. */ protected final int readInt() { return (read() << 24) | (read() << 16) | (read() << 8) | read(); } /** * Skips n bytes in the input stream. */ protected void skipFully(int n) throws IOException { while (n > 0) { int c = (int) skip(n); if (c <= 0) throw new EOFException("Error looking for paramter names in bytecode: unexpected end of file"); n -= c; } } protected final Member resolveMethod(int index) throws IOException, ClassNotFoundException, NoSuchMethodException { int oldPos = pos; try { Member m = (Member) cpool[index]; if (m == null) { pos = cpoolIndex[index]; Class owner = resolveClass(readShort()); NameAndType nt = resolveNameAndType(readShort()); String signature = nt.name + nt.type; if (nt.name.equals("")) { Constructor[] ctors = owner.getConstructors(); for (int i = 0; i < ctors.length; i++) { String sig = getSignature(ctors[i], ctors[i].getParameterTypes()); if (sig.equals(signature)) { cpool[index] = m = ctors[i]; return m; } } } else { Method[] methods = owner.getDeclaredMethods(); for (int i = 0; i < methods.length; i++) { String sig = getSignature(methods[i], methods[i].getParameterTypes()); if (sig.equals(signature)) { cpool[index] = m = methods[i]; return m; } } } throw new NoSuchMethodException(signature); } return m; } finally { pos = oldPos; } } protected final Field resolveField(int i) throws IOException, ClassNotFoundException, NoSuchFieldException { int oldPos = pos; try { Field f = (Field) cpool[i]; if (f == null) { pos = cpoolIndex[i]; Class owner = resolveClass(readShort()); NameAndType nt = resolveNameAndType(readShort()); cpool[i] = f = owner.getDeclaredField(nt.name); } return f; } finally { pos = oldPos; } } private static class NameAndType { String name; String type; public NameAndType(String name, String type) { this.name = name; this.type = type; } } protected final NameAndType resolveNameAndType(int i) throws IOException { int oldPos = pos; try { NameAndType nt = (NameAndType) cpool[i]; if (nt == null) { pos = cpoolIndex[i]; String name = resolveUtf8(readShort()); String type = resolveUtf8(readShort()); cpool[i] = nt = new NameAndType(name, type); } return nt; } finally { pos = oldPos; } } protected final Class resolveClass(int i) throws IOException, ClassNotFoundException { int oldPos = pos; try { Class c = (Class) cpool[i]; if (c == null) { pos = cpoolIndex[i]; String name = resolveUtf8(readShort()); cpool[i] = c = Class.forName(classDescriptorToName(name)); } return c; } finally { pos = oldPos; } } protected final String resolveUtf8(int i) throws IOException { int oldPos = pos; try { String s = (String) cpool[i]; if (s == null) { pos = cpoolIndex[i]; int len = readShort(); skipFully(len); cpool[i] = s = new String(buf, pos - len, len, "utf-8"); } return s; } finally { pos = oldPos; } } protected final void readCpool() throws IOException { int count = readShort(); // cpool count cpoolIndex = new int[count]; cpool = new Object[count]; for (int i = 1; i < count; i++) { int c = read(); cpoolIndex[i] = super.pos; switch (c) // constant pool tag { case CONSTANT_Fieldref: case CONSTANT_Methodref: case CONSTANT_InterfaceMethodref: case CONSTANT_NameAndType: readShort(); // class index or (12) name index // fall through case CONSTANT_Class: case CONSTANT_String: readShort(); // string index or class index break; case CONSTANT_Long: case CONSTANT_Double: readInt(); // hi-value // see jvm spec section 4.4.5 - double and long cpool // entries occupy two "slots" in the cpool table. i++; // fall through case CONSTANT_Integer: case CONSTANT_Float: readInt(); // value break; case CONSTANT_Utf8: int len = readShort(); skipFully(len); break; case CONSTANT_MethodHandle: read(); // reference kind readShort(); // reference index break; case CONSTANT_MethodType: readShort(); // descriptor index break; case CONSTANT_Dynamic: readShort(); // bootstrap method attr index readShort(); // name and type index break; case CONSTANT_InvokeDynamic: readShort(); // bootstrap method attr index readShort(); // name and type index break; default: // corrupt class file throw new IllegalStateException("Error looking for paramter names in bytecode: unexpected bytes in file, tag:"+c); } } } protected final void skipAttributes() throws IOException { int count = readShort(); for (int i = 0; i < count; i++) { readShort(); // name index skipFully(readInt()); } } /** * Reads an attributes array. The elements of a class file that * can contain attributes are: fields, methods, the class itself, * and some other types of attributes. */ protected final void readAttributes() throws IOException { int count = readShort(); for (int i = 0; i < count; i++) { int nameIndex = readShort(); // name index int attrLen = readInt(); int curPos = pos; String attrName = resolveUtf8(nameIndex); Method m = (Method) attrMethods.get(attrName); if (m != null) { try { m.invoke(this, new Object[]{}); } catch (IllegalAccessException e) { pos = curPos; skipFully(attrLen); } catch (InvocationTargetException e) { try { throw e.getTargetException(); } catch (Error ex) { throw ex; } catch (RuntimeException ex) { throw ex; } catch (IOException ex) { throw ex; } catch (Throwable ex) { pos = curPos; skipFully(attrLen); } } } else { // don't care what attribute this is skipFully(attrLen); } } } /** * Reads a code attribute. * * @throws IOException */ public void readCode() throws IOException { readShort(); // max stack readShort(); // max locals skipFully(readInt()); // code skipFully(8 * readShort()); // exception table // read the code attributes (recursive). This is where // we will find the LocalVariableTable attribute. readAttributes(); } protected ClassReader(byte buf[], Map attrMethods) { super(buf); this.attrMethods = attrMethods; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy