Author: mmerz Date: Wed Dec 22 10:12:38 2004 New Revision: 123132 URL: http://svn.apache.org/viewcvs?view=rev&rev=123132 Log: Fix for BEEHIVE-120 (@WebParam.name default values). Code for reading parameter names from debug information (util/bytecode/*) was taken from Apache Axis project.
Added: incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/util/bytecode/ incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/util/bytecode/ClassReader.java incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/util/bytecode/ParamNameExtractor.java incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/util/bytecode/ParamReader.java Modified: incubator/beehive/trunk/wsm/drt/tests/org/apache/beehive/wsm/jsr181/processor/apt/WsmAnnotationProcessorTest.java incubator/beehive/trunk/wsm/drt/tests/org/apache/beehive/wsm/jsr181/processor/reflection/WsmAnnotationProcessorTest.java incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/processor/reflection/WsmReflectionAnnotationProcessor.java Modified: incubator/beehive/trunk/wsm/drt/tests/org/apache/beehive/wsm/jsr181/processor/apt/WsmAnnotationProcessorTest.java Url: http://svn.apache.org/viewcvs/incubator/beehive/trunk/wsm/drt/tests/org/apache/beehive/wsm/jsr181/processor/apt/WsmAnnotationProcessorTest.java?view=diff&rev=123132&p1=incubator/beehive/trunk/wsm/drt/tests/org/apache/beehive/wsm/jsr181/processor/apt/WsmAnnotationProcessorTest.java&r1=123131&p2=incubator/beehive/trunk/wsm/drt/tests/org/apache/beehive/wsm/jsr181/processor/apt/WsmAnnotationProcessorTest.java&r2=123132 ============================================================================== --- incubator/beehive/trunk/wsm/drt/tests/org/apache/beehive/wsm/jsr181/processor/apt/WsmAnnotationProcessorTest.java (original) +++ incubator/beehive/trunk/wsm/drt/tests/org/apache/beehive/wsm/jsr181/processor/apt/WsmAnnotationProcessorTest.java Wed Dec 22 10:12:38 2004 @@ -63,7 +63,6 @@ /** * We can't derive default values for WebParam from binaries. Thus, we use * our own default name "in<number>". - */ @Override public void testWebParamGoLocoFirst() throws Exception { final int paramNo = 0; @@ -74,4 +73,5 @@ int.class, param.getJavaType()); } + */ } Modified: incubator/beehive/trunk/wsm/drt/tests/org/apache/beehive/wsm/jsr181/processor/reflection/WsmAnnotationProcessorTest.java Url: http://svn.apache.org/viewcvs/incubator/beehive/trunk/wsm/drt/tests/org/apache/beehive/wsm/jsr181/processor/reflection/WsmAnnotationProcessorTest.java?view=diff&rev=123132&p1=incubator/beehive/trunk/wsm/drt/tests/org/apache/beehive/wsm/jsr181/processor/reflection/WsmAnnotationProcessorTest.java&r1=123131&p2=incubator/beehive/trunk/wsm/drt/tests/org/apache/beehive/wsm/jsr181/processor/reflection/WsmAnnotationProcessorTest.java&r2=123132 ============================================================================== --- incubator/beehive/trunk/wsm/drt/tests/org/apache/beehive/wsm/jsr181/processor/reflection/WsmAnnotationProcessorTest.java (original) +++ incubator/beehive/trunk/wsm/drt/tests/org/apache/beehive/wsm/jsr181/processor/reflection/WsmAnnotationProcessorTest.java Wed Dec 22 10:12:38 2004 @@ -42,7 +42,6 @@ /** * We can't derive default values for WebParam from binaries. Thus, we use * our own default name "in<number>". - */ @Override public void testWebParamGoLocoFirst() throws Exception { final int paramNo = 0; @@ -53,5 +52,6 @@ int.class, param.getJavaType()); } + */ } Modified: incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/processor/reflection/WsmReflectionAnnotationProcessor.java Url: http://svn.apache.org/viewcvs/incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/processor/reflection/WsmReflectionAnnotationProcessor.java?view=diff&rev=123132&p1=incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/processor/reflection/WsmReflectionAnnotationProcessor.java&r1=123131&p2=incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/processor/reflection/WsmReflectionAnnotationProcessor.java&r2=123132 ============================================================================== --- incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/processor/reflection/WsmReflectionAnnotationProcessor.java (original) +++ incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/processor/reflection/WsmReflectionAnnotationProcessor.java Wed Dec 22 10:12:38 2004 @@ -126,20 +126,34 @@ * @param method * @return */ - protected Jsr181MethodMetadata getWebServiceMETHODMetadata(Method method) { + protected Jsr181MethodMetadata getWebServiceMETHODMetadata(Method method) + { List<Jsr181ParameterMetadata> webParams = new ArrayList<Jsr181ParameterMetadata>(); // publish all params Annotation[][] allAnnotations = method.getParameterAnnotations(); - int offset = 0; - for (Class paramType : method.getParameterTypes()) { - webParams.add(getWebServicePARAMETERMetadata(paramType, - offset, - Arrays.asList(allAnnotations[offset]))); - offset++; + final Class[] parameterTypes = method.getParameterTypes(); + if ((null != parameterTypes) && (0 < parameterTypes.length)) + { + final String[] defaultNames = + org.apache.beehive.wsm.jsr181.util.bytecode.ParamNameExtractor.getParameterNamesFromDebugInfo(method); + int offset = 0; + for (Class paramType : parameterTypes) { + String defaultName = "in" + offset; + if ((null != defaultNames) && (offset < defaultNames.length)) + { + defaultName = defaultNames[offset]; + } + webParams.add(getWebServicePARAMETERMetadata( + paramType, + defaultName, + Arrays.asList(allAnnotations[offset]) + )); + offset++; + } } - + // create & return webMethod Jsr181MethodMetadata wsmm = null; try { @@ -157,18 +171,18 @@ /** * @param paramType - * @param offset + * @param defaultName * @param annotations * @return */ - protected Jsr181ParameterMetadata getWebServicePARAMETERMetadata(Class paramType, int offset, List<Annotation> annotations) { + protected Jsr181ParameterMetadata getWebServicePARAMETERMetadata(Class paramType, String defaultName, List<Annotation> annotations) { + // create & return webParam Jsr181ParameterMetadata wspm = null; try { - wspm = new Jsr181ParameterMetadataImpl(paramType, - "in" + offset, // by default parameter should be IN - annotations); - } catch (Throwable t) { + wspm = new Jsr181ParameterMetadataImpl(paramType, defaultName, annotations); + } + catch (Throwable t) { t.printStackTrace(); // todo: proper error handling wspm = null; } Added: incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/util/bytecode/ClassReader.java Url: http://svn.apache.org/viewcvs/incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/util/bytecode/ClassReader.java?view=auto&rev=123132 ============================================================================== --- (empty file) +++ incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/util/bytecode/ClassReader.java Wed Dec 22 10:12:38 2004 @@ -0,0 +1,426 @@ +package org.apache.beehive.wsm.jsr181.util.bytecode; + +/* + * Copyright 2002-2004 The Apache Software Foundation. + * + * Licensed 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. + */ + +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. <p> + * + * 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. <p> + * + * don't get tricky, it's the bare minimum. Instances of this class + * are not threadsafe -- don't share them. <p> + * + * @author Edwin Smith, Macromedia + */ +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; + /** + * 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; + + /** + * load 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 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("cantLoadByecode for " + 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() : "<init>"); + 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 the next unsigned 16 bit value + */ + protected final int readShort() { + return (read() << 8) | read(); + } + + /** + * @return the next signed 32 bit value + */ + protected final int readInt() { + return (read() << 24) | (read() << 16) | (read() << 8) | read(); + } + + /** + * skip 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("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("<init>")) { + 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; + + default: + // corrupt class file + throw new IllegalStateException("Unexpected Byte"); + } + } + } + + protected final void skipAttributes() throws IOException { + int count = readShort(); + for (int i = 0; i < count; i++) { + readShort(); // name index + skipFully(readInt()); + } + } + + /** + * read 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); + } + } + } + + /** + * read 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; + } +} \ No newline at end of file Added: incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/util/bytecode/ParamNameExtractor.java Url: http://svn.apache.org/viewcvs/incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/util/bytecode/ParamNameExtractor.java?view=auto&rev=123132 ============================================================================== --- (empty file) +++ incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/util/bytecode/ParamNameExtractor.java Wed Dec 22 10:12:38 2004 @@ -0,0 +1,65 @@ +package org.apache.beehive.wsm.jsr181.util.bytecode; + +/* + * Copyright 2001-2004 The Apache Software Foundation. + * + * Licensed 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. + */ + +import java.io.IOException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +/** + * This class retieves function parameter names from bytecode built with + * debugging symbols. Used as a last resort when creating WSDL. + * + * @author <a href="mailto:[EMAIL PROTECTED]">Tom Jordahl</a> + */ +public class ParamNameExtractor { + +// protected static Log log = +// LogFactory.getLog(ParamNameExtractor.class.getName()); + + /** + * Retrieve a list of function parameter names from a method + * Returns null if unable to read parameter names (i.e. bytecode not + * built with debug). + */ + public static String[] getParameterNamesFromDebugInfo(Method method) { + // Don't worry about it if there are no params. + int numParams = method.getParameterTypes().length; + if (numParams == 0) + return null; + + // get declaring class + Class c = method.getDeclaringClass(); + + // Don't worry about it if the class is a Java dynamic proxy + if(Proxy.isProxyClass(c)) { + return null; + } + + try { + // get a parameter reader + ParamReader pr = new ParamReader(c); + // get the paramter names + String[] names = pr.getParameterNames(method); + return names; + } catch (IOException e) { + // log it and leave +// log.info(Messages.getMessage("error00") + ":" + e); + return null; + } + } +} Added: incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/util/bytecode/ParamReader.java Url: http://svn.apache.org/viewcvs/incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/util/bytecode/ParamReader.java?view=auto&rev=123132 ============================================================================== --- (empty file) +++ incubator/beehive/trunk/wsm/src/runtime/org/apache/beehive/wsm/jsr181/util/bytecode/ParamReader.java Wed Dec 22 10:12:38 2004 @@ -0,0 +1,223 @@ +package org.apache.beehive.wsm.jsr181.util.bytecode; + +/* + * Copyright 2002-2004 The Apache Software Foundation. + * + * Licensed 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. + */ + +import java.io.IOException; +import java.lang.reflect.Method; +import java.lang.reflect.Constructor; +import java.lang.reflect.Member; +import java.lang.reflect.Modifier; +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. <p> + * + * 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. <p> + * + * don't get tricky, it's the bare minimum. Instances of this class + * are not threadsafe -- don't share them. <p> + * + * @author Edwin Smith, Macromedia + */ +public class ParamReader + extends ClassReader { + private String methodName; + private Map methods = new HashMap(); + private Class[] paramTypes; + + /** + * process a class file, given it's class. We'll use the defining + * classloader to locate the bytecode. + * @param c + * @throws IOException + */ + public ParamReader(Class c) throws IOException { + this(getBytes(c)); + } + + /** + * process the given class bytes directly. + * @param b + * @throws IOException + */ + public ParamReader(byte[] b) throws IOException { + super(b, findAttributeReaders(ParamReader.class)); + + // check the magic number + if (readInt() != 0xCAFEBABE) { + // not a class file! + throw new IOException("badClassFile"); + } + + readShort(); // minor version + readShort(); // major version + + readCpool(); // slurp in the constant pool + + readShort(); // access flags + readShort(); // this class name + readShort(); // super class name + + int count = readShort(); // ifaces count + for (int i = 0; i < count; i++) { + readShort(); // interface index + } + + count = readShort(); // fields count + for (int i = 0; i < count; i++) { + readShort(); // access flags + readShort(); // name index + readShort(); // descriptor index + skipAttributes(); // field attributes + } + + count = readShort(); // methods count + for (int i = 0; i < count; i++) { + readShort(); // access flags + int m = readShort(); // name index + String name = resolveUtf8(m); + int d = readShort(); // descriptor index + this.methodName = name + resolveUtf8(d); + readAttributes(); // method attributes + } + + } + + public void readCode() throws IOException + { + readShort(); // max stack + int maxLocals = readShort(); // max locals + + MethodInfo info = new MethodInfo(maxLocals); + if (methods != null && methodName != null) + { + methods.put(methodName, info); + } + + skipFully(readInt()); // code + skipFully(8 * readShort()); // exception table + // read the code attributes (recursive). This is where + // we will find the LocalVariableTable attribute. + readAttributes(); + } + + /** + * return the names of the declared parameters for the given constructor. + * If we cannot determine the names, return null. The returned array will + * have one name per parameter. The length of the array will be the same + * as the length of the Class[] array returned by Constructor.getParameterTypes(). + * @param ctor + * @return String[] array of names, one per parameter, or null + */ + public String[] getParameterNames(Constructor ctor) { + paramTypes = ctor.getParameterTypes(); + return getParameterNames(ctor, paramTypes); + } + + /** + * return the names of the declared parameters for the given method. + * If we cannot determine the names, return null. The returned array will + * have one name per parameter. The length of the array will be the same + * as the length of the Class[] array returned by Method.getParameterTypes(). + * @param method + * @return String[] array of names, one per parameter, or null + */ + public String[] getParameterNames(Method method) { + paramTypes = method.getParameterTypes(); + return getParameterNames(method, paramTypes); + } + + protected String[] getParameterNames(Member member,Class [] paramTypes) { + // look up the names for this method + MethodInfo info = (MethodInfo) methods.get(getSignature(member, paramTypes)); + + // we know all the local variable names, but we only need to return + // the names of the parameters. + + if (info != null) { + String[] paramNames = new String[paramTypes.length]; + int j = Modifier.isStatic(member.getModifiers()) ? 0 : 1; + + boolean found = false; // did we find any non-null names + for (int i = 0; i < paramNames.length; i++) { + if (info.names[j] != null) { + found = true; + paramNames[i] = info.names[j]; + } + j++; + if (paramTypes[i] == double.class || paramTypes[i] == long.class) { + // skip a slot for 64bit params + j++; + } + } + + if (found) { + return paramNames; + } else { + return null; + } + } else { + return null; + } + } + + private static class MethodInfo + { + String[] names; + int maxLocals; + + public MethodInfo(int maxLocals) + { + this.maxLocals = maxLocals; + names = new String[maxLocals]; + } + } + + private MethodInfo getMethodInfo() + { + MethodInfo info = null; + if (methods != null && methodName != null) + { + info = (MethodInfo) methods.get(methodName); + } + return info; + } + + /** + * this is invoked when a LocalVariableTable attribute is encountered. + * @throws IOException + */ + public void readLocalVariableTable() throws IOException { + int len = readShort(); // table length + MethodInfo info = getMethodInfo(); + for (int j = 0; j < len; j++) { + readShort(); // start pc + readShort(); // length + int nameIndex = readShort(); // name_index + readShort(); // descriptor_index + int index = readShort(); // local index + if (info != null) { + info.names[index] = resolveUtf8(nameIndex); + } + } + } +} \ No newline at end of file
