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 }