001    /***
002     *
003     * Portions Copyright (c) 2007 Paul Hammant
004     * Portions copyright (c) 2000-2007 INRIA, France Telecom
005     * All rights reserved.
006     *
007     * Redistribution and use in source and binary forms, with or without
008     * modification, are permitted provided that the following conditions
009     * are met:
010     * 1. Redistributions of source code must retain the above copyright
011     *    notice, this list of conditions and the following disclaimer.
012     * 2. Redistributions in binary form must reproduce the above copyright
013     *    notice, this list of conditions and the following disclaimer in the
014     *    documentation and/or other materials provided with the distribution.
015     * 3. Neither the name of the copyright holders nor the names of its
016     *    contributors may be used to endorse or promote products derived from
017     *    this software without specific prior written permission.
018     *
019     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
020     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
021     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
022     * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
023     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
024     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
025     * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
026     * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
027     * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
028     * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
029     * THE POSSIBILITY OF SUCH DAMAGE.
030     */
031    
032    package com.thoughtworks.paranamer;
033    
034    import java.io.IOException;
035    import java.io.InputStream;
036    import java.io.File;
037    import java.lang.reflect.Constructor;
038    import java.lang.reflect.Method;
039    import java.lang.reflect.AccessibleObject;
040    import java.lang.reflect.Modifier;
041    import java.util.ArrayList;
042    import java.util.List;
043    import java.util.Map;
044    import java.util.HashMap;
045    
046    
047    /**
048     * An ASM-based implementation of Paranamer. It relies on debug information compiled
049     * with the "-g" javac option to retrieve parameter names.
050     * <p/>
051     * Portions of this source file are a fork of ASM.
052     *
053     * @author Guilherme Silveira
054     * @author Paul Hammant
055     */
056    public class BytecodeReadingParanamer implements Paranamer {
057    
058        private static final Map primitives = new HashMap() {
059            {
060                put("int","I");
061                put("boolean","Z");
062                put("char","C");
063                put("short","B");
064                put("float","F");
065                put("long","J");
066                put("double","D");
067            }
068        };
069    
070        public String[] lookupParameterNames(AccessibleObject methodOrConstructor) {
071            return lookupParameterNames(methodOrConstructor, true);
072        }
073    
074        public String[] lookupParameterNames(AccessibleObject methodOrCtor, boolean throwExceptionIfMissing) {
075    
076            Class[] types = null;
077            Class declaringClass = null;
078            String name = null;
079            if (methodOrCtor instanceof Method) {
080                Method method = (Method) methodOrCtor;
081                types = method.getParameterTypes();
082                name = method.getName();
083                declaringClass = method.getDeclaringClass();
084            } else {
085                Constructor constructor = (Constructor) methodOrCtor;
086                types = constructor.getParameterTypes();
087                declaringClass = constructor.getDeclaringClass();
088                name = "<init>";
089            }
090    
091            if (types.length == 0) {
092                return EMPTY_NAMES;
093            }
094            InputStream content = getClassAsStream(declaringClass);
095            if (content == null) {
096                if (throwExceptionIfMissing) {
097                    throw new ParameterNamesNotFoundException("Unable to get class bytes");
098                } else {
099                    return Paranamer.EMPTY_NAMES;
100                }
101            }
102            try {
103                ClassReader reader = new ClassReader(content);
104                TypeCollector visitor = new TypeCollector(name, types, throwExceptionIfMissing);
105                reader.accept(visitor);
106                return visitor.getParameterNamesForMethod();
107            } catch (IOException e) {
108                if (throwExceptionIfMissing) {
109                    throw new ParameterNamesNotFoundException("IoException while reading class bytes", e);
110                } else {
111                    return Paranamer.EMPTY_NAMES;
112                }
113            }
114        }
115    
116        public int areParameterNamesAvailable(Class clazz, String constructorOrMethodName) {
117            InputStream content = getClassAsStream(clazz.getClassLoader(), clazz.getName());
118            if (content == null) {
119                return NO_PARAMETER_NAMES_FOR_CLASS;
120            }
121            try {
122                ClassReader reader = new ClassReader(content);
123                TypeCollector visitor = null;
124                if (constructorOrMethodName.equals("<init>")) {
125                    visitor = new TypeCollector(constructorOrMethodName, (clazz.getConstructors()[0]).getParameterTypes(), true);
126                } else {
127                    List methods = getMatchingMethods(clazz.getClassLoader(), clazz.getName(), constructorOrMethodName);
128                    if (methods.size() == 0) {
129                        return Paranamer.NO_PARAMETER_NAMES_FOR_CLASS_AND_MEMBER;
130                    }
131                    visitor = new TypeCollector(constructorOrMethodName, ((Method) methods.get(0)).getParameterTypes(), true);
132                }
133                reader.accept(visitor);
134                if (visitor.isClassFound()) {
135                    if (!visitor.isMethodFound() || !visitor.isDebugInfoPresent()) {
136                        return Paranamer.NO_PARAMETER_NAMES_FOR_CLASS_AND_MEMBER;
137                    }
138                } else {
139                    return Paranamer.NO_PARAMETER_NAMES_FOR_CLASS;
140                }
141                return Paranamer.PARAMETER_NAMES_FOUND;
142            } catch (IOException e) {
143                return Paranamer.NO_PARAMETER_NAMES_FOR_CLASS;
144            } catch (ClassNotFoundException e) {
145                return NO_PARAMETER_NAMES_FOR_CLASS;
146            }
147        }
148    
149        private InputStream getClassAsStream(Class clazz) {
150            ClassLoader classLoader = clazz.getClassLoader();
151            if (classLoader == null) {
152                classLoader = ClassLoader.getSystemClassLoader();
153            }
154            return getClassAsStream(classLoader, clazz.getName());
155        }
156    
157        private InputStream getClassAsStream(ClassLoader classLoader, String className) {
158            String name = className.replace('.', '/') + ".class";
159            // better pre-cache all methods otherwise this content will be loaded
160            // multiple times
161            InputStream asStream = classLoader.getResourceAsStream(name);
162            if (asStream == null) {
163                asStream = BytecodeReadingParanamer.class.getResourceAsStream(name);
164            }
165            return asStream;
166        }
167    
168        private List getMatchingMethods(ClassLoader classLoader, String className, String name) throws ClassNotFoundException {
169            List list = new ArrayList();
170            Method[] methods = classLoader.loadClass(className).getMethods();
171            for (int i = 0; i < methods.length; i++) {
172                Method method = methods[i];
173                if (method.getName().equals(name)) {
174                    list.add(method);
175                }
176            }
177            return list;
178        }
179    
180        /**
181         * The type collector waits for an specific method in order to start a method
182         * collector.
183         *
184         * @author Guilherme Silveira
185         */
186        private static class TypeCollector {
187    
188            private static final String COMMA = ",";
189    
190            private final String methodName;
191    
192            private final Class[] parameterTypes;
193            private final boolean throwExceptionIfMissing;
194    
195            private MethodCollector collector;
196            private boolean methodFound = false;
197            private boolean classFound = false;
198    
199            private TypeCollector(String methodName, Class[] parameterTypes, boolean throwExceptionIfMissing) {
200                this.methodName = methodName;
201                this.parameterTypes = parameterTypes;
202                this.throwExceptionIfMissing = throwExceptionIfMissing;
203                this.collector = null;
204            }
205    
206            public MethodCollector visitMethod(int access, String name, String desc) {
207                // already found the method, skip any processing
208                classFound = true;
209                if (collector != null) {
210                    return null;
211                }
212                // not the same name
213                if (!name.equals(methodName)) {
214                    return null;
215                }
216                methodFound = true;
217                Type[] argumentTypes = Type.getArgumentTypes(desc);
218                int longOrDoubleQuantity = 0;
219                for (int i1 = 0; i1 < argumentTypes.length; i1++) {
220                    Type t = argumentTypes[i1];
221                    if (t.getClassName().equals("long")
222                            || t.getClassName().equals("double")) {
223                        longOrDoubleQuantity++;
224                    }
225                }
226                int paramCount = argumentTypes.length;
227                // not the same quantity of parameters
228                if (paramCount != this.parameterTypes.length) {
229                    return null;
230                }
231                for (int i = 0; i < argumentTypes.length; i++) {
232                    if (!correctTypeName(argumentTypes, i).equals(
233                            this.parameterTypes[i].getName())) {
234                        return null;
235                    }
236                }
237                this.collector = new MethodCollector((Modifier.isStatic(access) ? 0 : 1),
238                        argumentTypes.length + longOrDoubleQuantity);
239                return collector;
240            }
241    
242            private String correctTypeName(Type[] argumentTypes, int i) {
243                String s = argumentTypes[i].getClassName();
244                // array notation needs cleanup.
245                if (s.endsWith("[]")) {
246                    String prefix = s.substring(0, s.length() - 2);
247                    if (primitives.containsKey(prefix)) {
248                        s = "[" + primitives.get(prefix);
249                    } else {
250                    s = "[L" + prefix + ";";
251                    }
252                }
253                return s;
254            }
255    
256            private boolean isDebugInfoPresent() {
257                return collector.isDebugInfoPresent();
258            }
259    
260            private String[] getParameterNamesForMethod() {
261                if (collector == null) {
262                    return Paranamer.EMPTY_NAMES;
263                }
264                if (!collector.isDebugInfoPresent()) {
265                    if (throwExceptionIfMissing) {
266                        throw new ParameterNamesNotFoundException("Parameter names not found for " + methodName);
267                    } else {
268                        return Paranamer.EMPTY_NAMES;
269                    }
270                }
271                return collector.getResult().split(COMMA);
272            }
273    
274            private boolean isMethodFound() {
275                return methodFound;
276            }
277    
278            private boolean isClassFound() {
279                return classFound;
280            }
281        }
282    
283        /**
284         * Objects of this class collects information from a specific method.
285         *
286         * @author Guilherme Silveira
287         */
288        private static class MethodCollector {
289    
290            private final int paramCount;
291    
292            private final int ignoreCount;
293    
294            private int currentParameter;
295    
296            private final StringBuffer result;
297    
298            private boolean debugInfoPresent;
299    
300            private MethodCollector(int ignoreCount, int paramCount) {
301                this.ignoreCount = ignoreCount;
302                this.paramCount = paramCount;
303                this.result = new StringBuffer();
304                this.currentParameter = 0;
305                // if there are 0 parameters, there is no need for debug info
306                this.debugInfoPresent = paramCount == 0 ? true : false;
307            }
308    
309            public void visitLocalVariable(String name, int index) {
310                if (index >= ignoreCount && index < ignoreCount + paramCount) {
311                    if (!name.equals("arg" + currentParameter)) {
312                        debugInfoPresent = true;
313                    }
314                    result.append(',');
315                    result.append(name);
316                    currentParameter++;
317                }
318            }
319    
320            private String getResult() {
321                return result.length() != 0 ? result.substring(1) : "";
322            }
323    
324            private boolean isDebugInfoPresent() {
325                return debugInfoPresent;
326            }
327    
328        }
329    
330    /***
331     * Portions Copyright (c) 2007 Paul Hammant
332     * Portions copyright (c) 2000-2007 INRIA, France Telecom
333     * All rights reserved.
334     *
335     * Redistribution and use in source and binary forms, with or without
336     * modification, are permitted provided that the following conditions
337     * are met:
338     * 1. Redistributions of source code must retain the above copyright
339     *    notice, this list of conditions and the following disclaimer.
340     * 2. Redistributions in binary form must reproduce the above copyright
341     *    notice, this list of conditions and the following disclaimer in the
342     *    documentation and/or other materials provided with the distribution.
343     * 3. Neither the name of the copyright holders nor the names of its
344     *    contributors may be used to endorse or promote products derived from
345     *    this software without specific prior written permission.
346     *
347     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
348     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
349     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
350     * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
351     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
352     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
353     * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
354     * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
355     * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
356     * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
357     * THE POSSIBILITY OF SUCH DAMAGE.
358     */
359    
360    
361        /**
362         * A Java class parser to make a Class Visitor visit an existing class.
363         * This class parses a byte array conforming to the Java class file format and
364         * calls the appropriate visit methods of a given class visitor for each field,
365         * method and bytecode instruction encountered.
366         *
367         * @author Eric Bruneton
368         * @author Eugene Kuleshov
369         */
370        private static class ClassReader {
371    
372            /**
373             * The class to be parsed. <i>The content of this array must not be
374             * modified. This field is intended for Attribute sub classes, and
375             * is normally not needed by class generators or adapters.</i>
376             */
377            public final byte[] b;
378    
379            /**
380             * The start index of each constant pool item in {@link #b b}, plus one.
381             * The one byte offset skips the constant pool item tag that indicates its
382             * type.
383             */
384            private final int[] items;
385    
386            /**
387             * The String objects corresponding to the CONSTANT_Utf8 items. This cache
388             * avoids multiple parsing of a given CONSTANT_Utf8 constant pool item,
389             * which GREATLY improves performances (by a factor 2 to 3). This caching
390             * strategy could be extended to all constant pool items, but its benefit
391             * would not be so great for these items (because they are much less
392             * expensive to parse than CONSTANT_Utf8 items).
393             */
394            private final String[] strings;
395    
396            /**
397             * Maximum length of the strings contained in the constant pool of the
398             * class.
399             */
400            private final int maxStringLength;
401    
402            /**
403             * Start index of the class header information (access, name...) in
404             * {@link #b b}.
405             */
406            public final int header;
407    
408    
409            /**
410             * The type of CONSTANT_Fieldref constant pool items.
411             */
412            final static int FIELD = 9;
413    
414            /**
415             * The type of CONSTANT_Methodref constant pool items.
416             */
417            final static int METH = 10;
418    
419            /**
420             * The type of CONSTANT_InterfaceMethodref constant pool items.
421             */
422            final static int IMETH = 11;
423    
424            /**
425             * The type of CONSTANT_Integer constant pool items.
426             */
427            final static int INT = 3;
428    
429            /**
430             * The type of CONSTANT_Float constant pool items.
431             */
432            final static int FLOAT = 4;
433    
434            /**
435             * The type of CONSTANT_Long constant pool items.
436             */
437            final static int LONG = 5;
438    
439            /**
440             * The type of CONSTANT_Double constant pool items.
441             */
442            final static int DOUBLE = 6;
443    
444            /**
445             * The type of CONSTANT_NameAndType constant pool items.
446             */
447            final static int NAME_TYPE = 12;
448    
449            /**
450             * The type of CONSTANT_Utf8 constant pool items.
451             */
452            final static int UTF8 = 1;
453    
454            // ------------------------------------------------------------------------
455            // Constructors
456            // ------------------------------------------------------------------------
457    
458            /**
459             * Constructs a new {@link ClassReader} object.
460             *
461             * @param b the bytecode of the class to be read.
462             */
463            private ClassReader(final byte[] b) {
464                this(b, 0);
465            }
466    
467            /**
468             * Constructs a new {@link ClassReader} object.
469             *
470             * @param b   the bytecode of the class to be read.
471             * @param off the start offset of the class data.
472             */
473            private ClassReader(final byte[] b, final int off) {
474                this.b = b;
475                // parses the constant pool
476                items = new int[readUnsignedShort(off + 8)];
477                int n = items.length;
478                strings = new String[n];
479                int max = 0;
480                int index = off + 10;
481                for (int i = 1; i < n; ++i) {
482                    items[i] = index + 1;
483                    int size;
484                    switch (b[index]) {
485                        case FIELD:
486                        case METH:
487                        case IMETH:
488                        case INT:
489                        case FLOAT:
490                        case NAME_TYPE:
491                            size = 5;
492                            break;
493                        case LONG:
494                        case DOUBLE:
495                            size = 9;
496                            ++i;
497                            break;
498                        case UTF8:
499                            size = 3 + readUnsignedShort(index + 1);
500                            if (size > max) {
501                                max = size;
502                            }
503                            break;
504                            // case HamConstants.CLASS:
505                            // case HamConstants.STR:
506                        default:
507                            size = 3;
508                            break;
509                    }
510                    index += size;
511                }
512                maxStringLength = max;
513                // the class header information starts just after the constant pool
514                header = index;
515            }
516    
517    
518            /**
519             * Constructs a new {@link ClassReader} object.
520             *
521             * @param is an input stream from which to read the class.
522             * @throws IOException if a problem occurs during reading.
523             */
524            private ClassReader(final InputStream is) throws IOException {
525                this(readClass(is));
526            }
527    
528            /**
529             * Reads the bytecode of a class.
530             *
531             * @param is an input stream from which to read the class.
532             * @return the bytecode read from the given input stream.
533             * @throws IOException if a problem occurs during reading.
534             */
535            private static byte[] readClass(final InputStream is) throws IOException {
536                if (is == null) {
537                    throw new IOException("Class not found");
538                }
539                byte[] b = new byte[is.available()];
540                int len = 0;
541                while (true) {
542                    int n = is.read(b, len, b.length - len);
543                    if (n == -1) {
544                        if (len < b.length) {
545                            byte[] c = new byte[len];
546                            System.arraycopy(b, 0, c, 0, len);
547                            b = c;
548                        }
549                        return b;
550                    }
551                    len += n;
552                    if (len == b.length) {
553                        byte[] c = new byte[b.length + 1000];
554                        System.arraycopy(b, 0, c, 0, len);
555                        b = c;
556                    }
557                }
558            }
559    
560            // ------------------------------------------------------------------------
561            // Public methods
562            // ------------------------------------------------------------------------
563    
564            /**
565             * Makes the given visitor visit the Java class of this {@link ClassReader}.
566             * This class is the one specified in the constructor (see
567             * {@link #ClassReader(byte[]) ClassReader}).
568             *
569             * @param classVisitor the visitor that must visit this class.
570             */
571            private void accept(final TypeCollector classVisitor) {
572                char[] c = new char[maxStringLength]; // buffer used to read strings
573                int i, j, k; // loop variables
574                int u, v, w; // indexes in b
575    
576                String attrName;
577                int anns = 0;
578                int ianns = 0;
579    
580                // visits the header
581                u = header;
582                v = items[readUnsignedShort(u + 4)];
583                int len = readUnsignedShort(u + 6);
584                w = 0;
585                u += 8;
586                for (i = 0; i < len; ++i) {
587                    u += 2;
588                }
589                v = u;
590                i = readUnsignedShort(v);
591                v += 2;
592                for (; i > 0; --i) {
593                    j = readUnsignedShort(v + 6);
594                    v += 8;
595                    for (; j > 0; --j) {
596                        v += 6 + readInt(v + 2);
597                    }
598                }
599                i = readUnsignedShort(v);
600                v += 2;
601                for (; i > 0; --i) {
602                    j = readUnsignedShort(v + 6);
603                    v += 8;
604                    for (; j > 0; --j) {
605                        v += 6 + readInt(v + 2);
606                    }
607                }
608    
609                i = readUnsignedShort(v);
610                v += 2;
611                for (; i > 0; --i) {
612                    v += 6 + readInt(v + 2);
613                }
614    
615                // visits the class annotations
616                for (i = 1; i >= 0; --i) {
617                    v = i == 0 ? ianns : anns;
618                    if (v != 0) {
619                        v += 2;
620                    }
621                }
622    
623                // visits the fields
624                i = readUnsignedShort(u);
625                u += 2;
626                for (; i > 0; --i) {
627                    j = readUnsignedShort(u + 6);
628                    u += 8;
629                    for (; j > 0; --j) {
630                        u += 6 + readInt(u + 2);
631                    }
632                }
633    
634                // visits the methods
635                i = readUnsignedShort(u);
636                u += 2;
637                for (; i > 0; --i) {
638                    u = readMethod(classVisitor, c, u);
639                }
640    
641            }
642    
643            private int readMethod(TypeCollector classVisitor, char[] c, int u) {
644                int v;
645                int w;
646                int j;
647                String attrName;
648                int k;
649                int access = readUnsignedShort(u);
650                String name = readUTF8(u + 2, c);
651                String desc = readUTF8(u + 4, c);
652                v = 0;
653                w = 0;
654    
655                // looks for Code and Exceptions attributes
656                j = readUnsignedShort(u + 6);
657                u += 8;
658                for (; j > 0; --j) {
659                    attrName = readUTF8(u, c);
660                    int attrSize = readInt(u + 2);
661                    u += 6;
662                    // tests are sorted in decreasing frequency order
663                    // (based on frequencies observed on typical classes)
664                    if (attrName.equals("Code")) {
665                        v = u;
666                    }
667                    u += attrSize;
668                }
669                // reads declared exceptions
670                if (w == 0) {
671                } else {
672                    w += 2;
673                    for (j = 0; j < readUnsignedShort(w); ++j) {
674                        w += 2;
675                    }
676                }
677    
678                // visits the method's code, if any
679                MethodCollector mv = classVisitor.visitMethod(access, name, desc);
680    
681                if (mv != null && v != 0) {
682                    int codeLength = readInt(v + 4);
683                    v += 8;
684    
685                    int codeStart = v;
686                    int codeEnd = v + codeLength;
687                    v = codeEnd;
688    
689                    j = readUnsignedShort(v);
690                    v += 2;
691                    for (; j > 0; --j) {
692                        v += 8;
693                    }
694                    // parses the local variable, line number tables, and code
695                    // attributes
696                    int varTable = 0;
697                    int varTypeTable = 0;
698                    j = readUnsignedShort(v);
699                    v += 2;
700                    for (; j > 0; --j) {
701                        attrName = readUTF8(v, c);
702                        if (attrName.equals("LocalVariableTable")) {
703                            varTable = v + 6;
704                        } else if (attrName.equals("LocalVariableTypeTable")) {
705                            varTypeTable = v + 6;
706                        }
707                        v += 6 + readInt(v + 2);
708                    }
709    
710                    v = codeStart;
711                    // visits the local variable tables
712                    if (varTable != 0) {
713                        if (varTypeTable != 0) {
714                            k = readUnsignedShort(varTypeTable) * 3;
715                            w = varTypeTable + 2;
716                            int[] typeTable = new int[k];
717                            while (k > 0) {
718                                typeTable[--k] = w + 6; // signature
719                                typeTable[--k] = readUnsignedShort(w + 8); // index
720                                typeTable[--k] = readUnsignedShort(w); // start
721                                w += 10;
722                            }
723                        }
724                        k = readUnsignedShort(varTable);
725                        w = varTable + 2;
726                        for (; k > 0; --k) {
727                            int index = readUnsignedShort(w + 8);
728                            mv.visitLocalVariable(readUTF8(w + 4, c), index);
729                            w += 10;
730                        }
731                    }
732                }
733                return u;
734            }
735    
736            /**
737             * Reads an unsigned short value in {@link #b b}. <i>This method is
738             * intended for Attribute sub classes, and is normally not needed by
739             * class generators or adapters.</i>
740             *
741             * @param index the start index of the value to be read in {@link #b b}.
742             * @return the read value.
743             */
744            private int readUnsignedShort(final int index) {
745                byte[] b = this.b;
746                return ((b[index] & 0xFF) << 8) | (b[index + 1] & 0xFF);
747            }
748    
749            /**
750             * Reads a signed int value in {@link #b b}. <i>This method is intended for
751             * Attribute sub classes, and is normally not needed by class
752             * generators or adapters.</i>
753             *
754             * @param index the start index of the value to be read in {@link #b b}.
755             * @return the read value.
756             */
757            private int readInt(final int index) {
758                byte[] b = this.b;
759                return ((b[index] & 0xFF) << 24) | ((b[index + 1] & 0xFF) << 16)
760                        | ((b[index + 2] & 0xFF) << 8) | (b[index + 3] & 0xFF);
761            }
762    
763            /**
764             * Reads an UTF8 string constant pool item in {@link #b b}. <i>This method
765             * is intended for Attribute sub classes, and is normally not needed
766             * by class generators or adapters.</i>
767             *
768             * @param index the start index of an unsigned short value in {@link #b b},
769             *              whose value is the index of an UTF8 constant pool item.
770             * @param buf   buffer to be used to read the item. This buffer must be
771             *              sufficiently large. It is not automatically resized.
772             * @return the String corresponding to the specified UTF8 item.
773             */
774            private String readUTF8(int index, final char[] buf) {
775                int item = readUnsignedShort(index);
776                String s = strings[item];
777                if (s != null) {
778                    return s;
779                }
780                index = items[item];
781                return strings[item] = readUTF(index + 2, readUnsignedShort(index), buf);
782            }
783    
784            /**
785             * Reads UTF8 string in {@link #b b}.
786             *
787             * @param index  start offset of the UTF8 string to be read.
788             * @param utfLen length of the UTF8 string to be read.
789             * @param buf    buffer to be used to read the string. This buffer must be
790             *               sufficiently large. It is not automatically resized.
791             * @return the String corresponding to the specified UTF8 string.
792             */
793            private String readUTF(int index, final int utfLen, final char[] buf) {
794                int endIndex = index + utfLen;
795                byte[] b = this.b;
796                int strLen = 0;
797                int c, d, e;
798                while (index < endIndex) {
799                    c = b[index++] & 0xFF;
800                    switch (c >> 4) {
801                        case 0:
802                        case 1:
803                        case 2:
804                        case 3:
805                        case 4:
806                        case 5:
807                        case 6:
808                        case 7:
809                            // 0xxxxxxx
810                            buf[strLen++] = (char) c;
811                            break;
812                        case 12:
813                        case 13:
814                            // 110x xxxx 10xx xxxx
815                            d = b[index++];
816                            buf[strLen++] = (char) (((c & 0x1F) << 6) | (d & 0x3F));
817                            break;
818                        default:
819                            // 1110 xxxx 10xx xxxx 10xx xxxx
820                            d = b[index++];
821                            e = b[index++];
822                            buf[strLen++] = (char) (((c & 0x0F) << 12)
823                                    | ((d & 0x3F) << 6) | (e & 0x3F));
824                            break;
825                    }
826                }
827                return new String(buf, 0, strLen);
828            }
829    
830        }
831    
832    /***
833     * Portions Copyright (c) 2007 Paul Hammant
834     * Portions copyright (c) 2000-2007 INRIA, France Telecom
835     * All rights reserved.
836     *
837     * Redistribution and use in source and binary forms, with or without
838     * modification, are permitted provided that the following conditions
839     * are met:
840     * 1. Redistributions of source code must retain the above copyright
841     *    notice, this list of conditions and the following disclaimer.
842     * 2. Redistributions in binary form must reproduce the above copyright
843     *    notice, this list of conditions and the following disclaimer in the
844     *    documentation and/or other materials provided with the distribution.
845     * 3. Neither the name of the copyright holders nor the names of its
846     *    contributors may be used to endorse or promote products derived from
847     *    this software without specific prior written permission.
848     *
849     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
850     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
851     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
852     * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
853     * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
854     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
855     * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
856     * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
857     * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
858     * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
859     * THE POSSIBILITY OF SUCH DAMAGE.
860     */
861    
862        /**
863         * A Java type. This class can be used to make it easier to manipulate type and
864         * method descriptors.
865         *
866         * @author Eric Bruneton
867         * @author Chris Nokleberg
868         */
869        private static class Type {
870    
871            /**
872             * The sort of the <tt>void</tt> type.
873             */
874            private final static int VOID = 0;
875    
876            /**
877             * The sort of the <tt>boolean</tt> type.
878             */
879            private final static int BOOLEAN = 1;
880    
881            /**
882             * The sort of the <tt>char</tt> type.
883             */
884            private final static int CHAR = 2;
885    
886            /**
887             * The sort of the <tt>byte</tt> type.
888             */
889            private final static int BYTE = 3;
890    
891            /**
892             * The sort of the <tt>short</tt> type.
893             */
894            private final static int SHORT = 4;
895    
896            /**
897             * The sort of the <tt>int</tt> type.
898             */
899            private final static int INT = 5;
900    
901            /**
902             * The sort of the <tt>float</tt> type.
903             */
904            private final static int FLOAT = 6;
905    
906            /**
907             * The sort of the <tt>long</tt> type.
908             */
909            private final static int LONG = 7;
910    
911            /**
912             * The sort of the <tt>double</tt> type.
913             */
914            private final static int DOUBLE = 8;
915    
916            /**
917             * The sort of array reference types.
918             */
919            private final static int ARRAY = 9;
920    
921            /**
922             * The sort of object reference type.
923             */
924            private final static int OBJECT = 10;
925    
926            /**
927             * The <tt>void</tt> type.
928             */
929            private final static Type VOID_TYPE = new Type(VOID);
930    
931            /**
932             * The <tt>boolean</tt> type.
933             */
934            private final static Type BOOLEAN_TYPE = new Type(BOOLEAN);
935    
936            /**
937             * The <tt>char</tt> type.
938             */
939            private final static Type CHAR_TYPE = new Type(CHAR);
940    
941            /**
942             * The <tt>byte</tt> type.
943             */
944            private final static Type BYTE_TYPE = new Type(BYTE);
945    
946            /**
947             * The <tt>short</tt> type.
948             */
949            private final static Type SHORT_TYPE = new Type(SHORT);
950    
951            /**
952             * The <tt>int</tt> type.
953             */
954            private final static Type INT_TYPE = new Type(INT);
955    
956            /**
957             * The <tt>float</tt> type.
958             */
959            private final static Type FLOAT_TYPE = new Type(FLOAT);
960    
961            /**
962             * The <tt>long</tt> type.
963             */
964            private final static Type LONG_TYPE = new Type(LONG);
965    
966            /**
967             * The <tt>double</tt> type.
968             */
969            private final static Type DOUBLE_TYPE = new Type(DOUBLE);
970    
971            // ------------------------------------------------------------------------
972            // Fields
973            // ------------------------------------------------------------------------
974    
975            /**
976             * The sort of this Java type.
977             */
978            private final int sort;
979    
980            /**
981             * A buffer containing the internal name of this Java type. This field is
982             * only used for reference types.
983             */
984            private char[] buf;
985    
986            /**
987             * The offset of the internal name of this Java type in {@link #buf buf}.
988             * This field is only used for reference types.
989             */
990            private int off;
991    
992            /**
993             * The length of the internal name of this Java type. This field is only
994             * used for reference types.
995             */
996            private int len;
997    
998            // ------------------------------------------------------------------------
999            // Constructors
1000            // ------------------------------------------------------------------------
1001    
1002            /**
1003             * Constructs a primitive type.
1004             *
1005             * @param sort the sort of the primitive type to be constructed.
1006             */
1007            private Type(final int sort) {
1008                this.sort = sort;
1009                this.len = 1;
1010            }
1011    
1012            /**
1013             * Constructs a reference type.
1014             *
1015             * @param sort the sort of the reference type to be constructed.
1016             * @param buf  a buffer containing the descriptor of the previous type.
1017             * @param off  the offset of this descriptor in the previous buffer.
1018             * @param len  the length of this descriptor.
1019             */
1020            private Type(final int sort, final char[] buf, final int off, final int len) {
1021                this.sort = sort;
1022                this.buf = buf;
1023                this.off = off;
1024                this.len = len;
1025            }
1026    
1027    
1028            /**
1029             * Returns the Java types corresponding to the argument types of the given
1030             * method descriptor.
1031             *
1032             * @param methodDescriptor a method descriptor.
1033             * @return the Java types corresponding to the argument types of the given
1034             *         method descriptor.
1035             */
1036            private static Type[] getArgumentTypes(final String methodDescriptor) {
1037                char[] buf = methodDescriptor.toCharArray();
1038                int off = 1;
1039                int size = 0;
1040                while (true) {
1041                    char car = buf[off++];
1042                    if (car == ')') {
1043                        break;
1044                    } else if (car == 'L') {
1045                        while (buf[off++] != ';') {
1046                        }
1047                        ++size;
1048                    } else if (car != '[') {
1049                        ++size;
1050                    }
1051                }
1052    
1053                Type[] args = new Type[size];
1054                off = 1;
1055                size = 0;
1056                while (buf[off] != ')') {
1057                    args[size] = getType(buf, off);
1058                    off += args[size].len + (args[size].sort == OBJECT ? 2 : 0);
1059                    size += 1;
1060                }
1061                return args;
1062            }
1063    
1064    
1065            /**
1066             * Returns the Java type corresponding to the given type descriptor.
1067             *
1068             * @param buf a buffer containing a type descriptor.
1069             * @param off the offset of this descriptor in the previous buffer.
1070             * @return the Java type corresponding to the given type descriptor.
1071             */
1072            private static Type getType(final char[] buf, final int off) {
1073                int len;
1074                switch (buf[off]) {
1075                    case 'V':
1076                        return VOID_TYPE;
1077                    case 'Z':
1078                        return BOOLEAN_TYPE;
1079                    case 'C':
1080                        return CHAR_TYPE;
1081                    case 'B':
1082                        return BYTE_TYPE;
1083                    case 'S':
1084                        return SHORT_TYPE;
1085                    case 'I':
1086                        return INT_TYPE;
1087                    case 'F':
1088                        return FLOAT_TYPE;
1089                    case 'J':
1090                        return LONG_TYPE;
1091                    case 'D':
1092                        return DOUBLE_TYPE;
1093                    case '[':
1094                        len = 1;
1095                        while (buf[off + len] == '[') {
1096                            ++len;
1097                        }
1098                        if (buf[off + len] == 'L') {
1099                            ++len;
1100                            while (buf[off + len] != ';') {
1101                                ++len;
1102                            }
1103                        }
1104                        return new Type(ARRAY, buf, off, len + 1);
1105                        // case 'L':
1106                    default:
1107                        len = 1;
1108                        while (buf[off + len] != ';') {
1109                            ++len;
1110                        }
1111                        return new Type(OBJECT, buf, off + 1, len - 1);
1112                }
1113            }
1114    
1115            // ------------------------------------------------------------------------
1116            // Accessors
1117            // ------------------------------------------------------------------------
1118    
1119            /**
1120             * Returns the number of dimensions of this array type. This method should
1121             * only be used for an array type.
1122             *
1123             * @return the number of dimensions of this array type.
1124             */
1125            private int getDimensions() {
1126                int i = 1;
1127                while (buf[off + i] == '[') {
1128                    ++i;
1129                }
1130                return i;
1131            }
1132    
1133            /**
1134             * Returns the type of the elements of this array type. This method should
1135             * only be used for an array type.
1136             *
1137             * @return Returns the type of the elements of this array type.
1138             */
1139            private Type getElementType() {
1140                return getType(buf, off + getDimensions());
1141            }
1142    
1143            /**
1144             * Returns the name of the class corresponding to this type.
1145             *
1146             * @return the fully qualified name of the class corresponding to this type.
1147             */
1148            private String getClassName() {
1149                switch (sort) {
1150                    case VOID:
1151                        return "void";
1152                    case BOOLEAN:
1153                        return "boolean";
1154                    case CHAR:
1155                        return "char";
1156                    case BYTE:
1157                        return "byte";
1158                    case SHORT:
1159                        return "short";
1160                    case INT:
1161                        return "int";
1162                    case FLOAT:
1163                        return "float";
1164                    case LONG:
1165                        return "long";
1166                    case DOUBLE:
1167                        return "double";
1168                    case ARRAY:
1169                        StringBuffer b = new StringBuffer(getElementType().getClassName());
1170                        for (int i = getDimensions(); i > 0; --i) {
1171                            b.append("[]");
1172                        }
1173                        return b.toString();
1174                        // case OBJECT:
1175                    default:
1176                        return new String(buf, off, len).replace('/', '.');
1177                }
1178            }
1179        }
1180    
1181    
1182    }