001    package org.bridj.util;
002    import org.bridj.Platform;
003    import java.io.ByteArrayOutputStream;
004    import java.io.FileNotFoundException;
005    import java.io.IOException;
006    import java.io.InputStream;
007    import java.lang.reflect.Modifier;
008    import java.net.URL;
009    import java.util.ArrayList;
010    import java.util.Arrays;
011    import java.util.EnumSet;
012    import java.util.List;
013    
014    import org.objectweb.asm.*;
015    import static org.objectweb.asm.ClassReader.*;
016    /**
017     * Util class that scavenges through a class' bytecode to retrieve the original order of fields and methods, as defined in the sources (unlike what the reflection APIs return : they don't guarantee the order).
018     * @author ochafik
019     */
020    public final class BytecodeAnalyzer {
021        private BytecodeAnalyzer() {}
022        
023        /**
024         * Returns array of String[] { declaringClassInternalName, methodName, signature }
025         */
026        public static List<String[]> getNativeMethodSignatures(Class c) throws IOException {
027            return getNativeMethodSignatures(getInternalName(c), Platform.getClassLoader(c));
028        }
029        /**
030         * Returns array of String[] { declaringClassInternalName, methodName, signature }
031         */
032        public static List<String[]> getNativeMethodSignatures(String internalName, ClassLoader classLoader) throws IOException {
033            return getNativeMethodSignatures(internalName, classLoader, new ArrayList<String[]>());
034        }
035        private static List<String[]> getNativeMethodSignatures(final String internalName, ClassLoader classLoader, final List<String[]> ret) throws IOException {
036            ClassReader r = new ClassReader(readByteCode(internalName, classLoader));
037            String p = r.getSuperName();
038            if (p != null && !p.equals("java/lang/Object"))
039                getNativeMethodSignatures(p, classLoader, ret);
040            
041            r.accept(new EmptyVisitor() {
042                @Override
043                public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
044                    if (Modifier.isNative(access)) {
045                        ret.add(new String[] { internalName, name, desc });
046                    }
047                    return null;
048                }
049            }, SKIP_DEBUG | SKIP_FRAMES | SKIP_CODE);
050            
051            return ret;
052        }
053        
054        private static List<String> getFieldNames(final String internalName, String recurseToInternalName, ClassLoader classLoader, final List<String> ret) throws IOException {
055            ClassReader r = new ClassReader(readByteCode(internalName, classLoader));
056            String p = r.getSuperName();
057            if (p != null && !p.equals("java/lang/Object") && !recurseToInternalName.equals(internalName))
058                getFieldNames(p, recurseToInternalName, classLoader, ret);
059            
060            r.accept(new EmptyVisitor() {
061                @Override
062                public FieldVisitor visitField(int i, String name, String string1, String string2, Object o) {
063                    ret.add(name);
064                    return null;
065                }
066            }, SKIP_DEBUG | SKIP_FRAMES | SKIP_CODE);
067            
068            return ret;
069        }
070        
071        private static List<String> getMethodNames(final String internalName, String recurseToInternalName, ClassLoader classLoader, final List<String> ret) throws IOException {
072            ClassReader r = new ClassReader(readByteCode(internalName, classLoader));
073            String p = r.getSuperName();
074            if (p != null && !p.equals("java/lang/Object") && !recurseToInternalName.equals(internalName))
075                getMethodNames(p, recurseToInternalName, classLoader, ret);
076            
077            r.accept(new EmptyVisitor() {
078                @Override
079                public MethodVisitor visitMethod(int i, String name, String string1, String string2, String[] strings) {
080                    ret.add(name);
081                    return null;
082                }
083            }, SKIP_DEBUG | SKIP_FRAMES | SKIP_CODE);
084            
085            return ret;
086        }
087        
088        static String getInternalName(Class c) {
089            return c.getName().replace('.', '/');
090        }
091        static URL getClassResource(Class c) throws FileNotFoundException {
092            return getClassResource(getInternalName(c), Platform.getClassLoader(c));
093        }
094        static URL getClassResource(String internalClassName, ClassLoader classLoader) throws FileNotFoundException {
095            String p = internalClassName + ".class";
096            URL u = classLoader.getResource(p);
097            if (u == null)
098                throw new FileNotFoundException("Resource '" + p + "'");
099            return u;
100        }
101        static byte[] readByteCode(String classInternalName, ClassLoader classLoader) throws FileNotFoundException, IOException {
102            return readBytes(getClassResource(classInternalName, classLoader).openStream(), true);
103        }
104        static byte[] readBytes(InputStream in, boolean close) throws IOException {
105            ByteArrayOutputStream out = new ByteArrayOutputStream();
106            byte[] b = new byte[1024];
107            int len;
108            while ((len = in.read(b)) > 0) {
109                out.write(b, 0, len);
110            }
111            if (close)
112                in.close();
113            return out.toByteArray();
114        }
115        
116        public static List<String> getFieldNames(Class c, Class recurseTo) throws IOException {
117            return getFieldNames(getInternalName(c), getInternalName(recurseTo), Platform.getClassLoader(c), new ArrayList<String>());
118        }
119        public static List<String> getMethodNames(Class c, Class recurseTo) throws IOException {
120            return getMethodNames(getInternalName(c), getInternalName(recurseTo), Platform.getClassLoader(c), new ArrayList<String>());
121        }
122        static class EmptyVisitor extends ClassVisitor {
123    
124            public EmptyVisitor() {
125                super(Opcodes.ASM4);
126            }
127            public void visit(int i, int i1, String string, String string1, String string2, String[] strings) {
128                
129            }
130    
131            public void visitSource(String string, String string1) {
132                
133            }
134    
135            public void visitOuterClass(String string, String string1, String string2) {
136                
137            }
138    
139            public AnnotationVisitor visitAnnotation(String string, boolean bln) {
140                return null;
141            }
142    
143            public void visitAttribute(Attribute atrbt) {
144                
145            }
146    
147            public void visitInnerClass(String string, String string1, String string2, int i) {
148                
149            }
150    
151            public FieldVisitor visitField(int i, String string, String string1, String string2, Object o) {
152                return null;
153            }
154    
155            public MethodVisitor visitMethod(int i, String string, String string1, String string2, String[] strings) {
156                return null;
157            }
158    
159            public void visitEnd() {
160                
161            }
162            
163        }
164    }