001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019 020package org.apache.bytecode; 021 022 023import java.io.IOException; 024import java.lang.reflect.Constructor; 025import java.lang.reflect.Member; 026import java.lang.reflect.Method; 027import java.lang.reflect.Modifier; 028import java.util.HashMap; 029import java.util.Map; 030 031 032/** 033 * This is the class file reader for obtaining the parameter names 034 * for declared methods in a class. The class must have debugging 035 * attributes for us to obtain this information. <p> 036 * <p/> 037 * This does not work for inherited methods. To obtain parameter 038 * names for inherited methods, you must use a paramReader for the 039 * class that originally declared the method. <p> 040 * <p/> 041 * don't get tricky, it's the bare minimum. Instances of this class 042 * are not threadsafe -- don't share them. <p> 043 */ 044public class ParamReader 045 extends ClassReader { 046 private String methodName; 047 private Map methods = new HashMap(); 048 private Class[] paramTypes; 049 050 /** 051 * Processes a class file, given it's class. We'll use the defining 052 * classloader to locate the bytecode. 053 * 054 * @param c 055 * @throws java.io.IOException 056 */ 057 public ParamReader(Class c) throws IOException { 058 this(getBytes(c)); 059 } 060 061 /** 062 * Processes the given class bytes directly. 063 * 064 * @param b 065 * @throws IOException 066 */ 067 public ParamReader(byte[] b) throws IOException { 068 super(b, findAttributeReaders(ParamReader.class)); 069 070 // check the magic number 071 if (readInt() != 0xCAFEBABE) { 072 // not a class file! 073 throw new IOException("Error looking for paramter names in bytecode: input does not appear to be a valid class file"); 074 } 075 076 readShort(); // minor version 077 readShort(); // major version 078 079 readCpool(); // slurp in the constant pool 080 081 readShort(); // access flags 082 readShort(); // this class name 083 readShort(); // super class name 084 085 int count = readShort(); // ifaces count 086 for (int i = 0; i < count; i++) { 087 readShort(); // interface index 088 } 089 090 count = readShort(); // fields count 091 for (int i = 0; i < count; i++) { 092 readShort(); // access flags 093 readShort(); // name index 094 readShort(); // descriptor index 095 skipAttributes(); // field attributes 096 } 097 098 count = readShort(); // methods count 099 for (int i = 0; i < count; i++) { 100 readShort(); // access flags 101 int m = readShort(); // name index 102 String name = resolveUtf8(m); 103 int d = readShort(); // descriptor index 104 this.methodName = name + resolveUtf8(d); 105 readAttributes(); // method attributes 106 } 107 108 } 109 110 public void readCode() throws IOException { 111 readShort(); // max stack 112 int maxLocals = readShort(); // max locals 113 114 MethodInfo info = new MethodInfo(maxLocals); 115 if (methods != null && methodName != null) { 116 methods.put(methodName, info); 117 } 118 119 skipFully(readInt()); // code 120 skipFully(8 * readShort()); // exception table 121 // read the code attributes (recursive). This is where 122 // we will find the LocalVariableTable attribute. 123 readAttributes(); 124 } 125 126 /** 127 * Returns the names of the declared parameters for the given constructor. 128 * If we cannot determine the names, return null. The returned array will 129 * have one name per parameter. The length of the array will be the same 130 * as the length of the Class[] array returned by Constructor.getParameterTypes(). 131 * 132 * @param ctor 133 * @return Returns String[] array of names, one per parameter, or null 134 */ 135 public String[] getParameterNames(Constructor ctor) { 136 paramTypes = ctor.getParameterTypes(); 137 return getParameterNames(ctor, paramTypes); 138 } 139 140 /** 141 * Returns the names of the declared parameters for the given method. 142 * If we cannot determine the names, return null. The returned array will 143 * have one name per parameter. The length of the array will be the same 144 * as the length of the Class[] array returned by Method.getParameterTypes(). 145 * 146 * @param method 147 * @return Returns String[] array of names, one per parameter, or null 148 */ 149 public String[] getParameterNames(Method method) { 150 paramTypes = method.getParameterTypes(); 151 return getParameterNames(method, paramTypes); 152 } 153 154 protected String[] getParameterNames(Member member, Class [] paramTypes) { 155 // look up the names for this method 156 MethodInfo info = (MethodInfo) methods.get(getSignature(member, paramTypes)); 157 158 // we know all the local variable names, but we only need to return 159 // the names of the parameters. 160 161 if (info != null) { 162 String[] paramNames = new String[paramTypes.length]; 163 int j = Modifier.isStatic(member.getModifiers()) ? 0 : 1; 164 165 boolean found = false; // did we find any non-null names 166 for (int i = 0; i < paramNames.length; i++) { 167 if (info.names[j] != null) { 168 found = true; 169 paramNames[i] = info.names[j]; 170 } 171 j++; 172 if (paramTypes[i] == double.class || paramTypes[i] == long.class) { 173 // skip a slot for 64bit params 174 j++; 175 } 176 } 177 178 if (found) { 179 return paramNames; 180 } else { 181 return null; 182 } 183 } else { 184 return null; 185 } 186 } 187 188 private static class MethodInfo { 189 String[] names; 190 int maxLocals; 191 192 public MethodInfo(int maxLocals) { 193 this.maxLocals = maxLocals; 194 names = new String[maxLocals]; 195 } 196 } 197 198 private MethodInfo getMethodInfo() { 199 MethodInfo info = null; 200 if (methods != null && methodName != null) { 201 info = (MethodInfo) methods.get(methodName); 202 } 203 return info; 204 } 205 206 /** 207 * This is invoked when a LocalVariableTable attribute is encountered. 208 * 209 * @throws IOException 210 */ 211 public void readLocalVariableTable() throws IOException { 212 int len = readShort(); // table length 213 MethodInfo info = getMethodInfo(); 214 for (int j = 0; j < len; j++) { 215 readShort(); // start pc 216 readShort(); // length 217 int nameIndex = readShort(); // name_index 218 readShort(); // descriptor_index 219 int index = readShort(); // local index 220 if (info != null) { 221 info.names[index] = resolveUtf8(nameIndex); 222 } 223 } 224 } 225}