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}