001/*
002 * BridJ - Dynamic and blazing-fast native interop for Java.
003 * http://bridj.googlecode.com/
004 *
005 * Copyright (c) 2010-2013, Olivier Chafik (http://ochafik.com/)
006 * All rights reserved.
007 *
008 * Redistribution and use in source and binary forms, with or without
009 * modification, are permitted provided that the following conditions are met:
010 * 
011 *     * Redistributions of source code must retain the above copyright
012 *       notice, this list of conditions and the following disclaimer.
013 *     * Redistributions in binary form must reproduce the above copyright
014 *       notice, this list of conditions and the following disclaimer in the
015 *       documentation and/or other materials provided with the distribution.
016 *     * Neither the name of Olivier Chafik nor the
017 *       names of its contributors may be used to endorse or promote products
018 *       derived from this software without specific prior written permission.
019 * 
020 * THIS SOFTWARE IS PROVIDED BY OLIVIER CHAFIK AND CONTRIBUTORS ``AS IS'' AND ANY
021 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
022 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
023 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
024 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
025 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
026 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
027 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
028 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
029 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
030 */
031package org.bridj.util;
032
033import org.bridj.Platform;
034import java.io.ByteArrayOutputStream;
035import java.io.FileNotFoundException;
036import java.io.IOException;
037import java.io.InputStream;
038import java.lang.reflect.Modifier;
039import java.net.URL;
040import java.util.ArrayList;
041import java.util.Arrays;
042import java.util.EnumSet;
043import java.util.List;
044
045import org.objectweb.asm.*;
046import static org.objectweb.asm.ClassReader.*;
047
048/**
049 * Util class that scavenges through a class' bytecode to retrieve the original
050 * order of fields and methods, as defined in the sources (unlike what the
051 * reflection APIs return : they don't guarantee the order).
052 *
053 * @author ochafik
054 */
055public final class BytecodeAnalyzer {
056
057    private BytecodeAnalyzer() {
058    }
059
060    /**
061     * Returns array of String[] { declaringClassInternalName, methodName,
062     * signature }
063     */
064    public static List<String[]> getNativeMethodSignatures(Class c) throws IOException {
065        return getNativeMethodSignatures(getInternalName(c), Platform.getClassLoader(c));
066    }
067
068    /**
069     * Returns array of String[] { declaringClassInternalName, methodName,
070     * signature }
071     */
072    public static List<String[]> getNativeMethodSignatures(String internalName, ClassLoader classLoader) throws IOException {
073        return getNativeMethodSignatures(internalName, classLoader, new ArrayList<String[]>());
074    }
075
076    private static List<String[]> getNativeMethodSignatures(final String internalName, ClassLoader classLoader, final List<String[]> ret) throws IOException {
077        ClassReader r = new ClassReader(readByteCode(internalName, classLoader));
078        String p = r.getSuperName();
079        if (p != null && !p.equals("java/lang/Object")) {
080            getNativeMethodSignatures(p, classLoader, ret);
081        }
082
083        r.accept(new EmptyVisitor() {
084            @Override
085            public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
086                if (Modifier.isNative(access)) {
087                    ret.add(new String[]{internalName, name, desc});
088                }
089                return null;
090            }
091        }, SKIP_DEBUG | SKIP_FRAMES | SKIP_CODE);
092
093        return ret;
094    }
095
096    private static List<String> getFieldNames(final String internalName, String recurseToInternalName, ClassLoader classLoader, final List<String> ret) throws IOException {
097        ClassReader r = new ClassReader(readByteCode(internalName, classLoader));
098        String p = r.getSuperName();
099        if (p != null && !p.equals("java/lang/Object") && !recurseToInternalName.equals(internalName)) {
100            getFieldNames(p, recurseToInternalName, classLoader, ret);
101        }
102
103        r.accept(new EmptyVisitor() {
104            @Override
105            public FieldVisitor visitField(int i, String name, String string1, String string2, Object o) {
106                ret.add(name);
107                return null;
108            }
109        }, SKIP_DEBUG | SKIP_FRAMES | SKIP_CODE);
110
111        return ret;
112    }
113
114    private static List<String> getMethodNames(final String internalName, String recurseToInternalName, ClassLoader classLoader, final List<String> ret) throws IOException {
115        ClassReader r = new ClassReader(readByteCode(internalName, classLoader));
116        String p = r.getSuperName();
117        if (p != null && !p.equals("java/lang/Object") && !recurseToInternalName.equals(internalName)) {
118            getMethodNames(p, recurseToInternalName, classLoader, ret);
119        }
120
121        r.accept(new EmptyVisitor() {
122            @Override
123            public MethodVisitor visitMethod(int i, String name, String string1, String string2, String[] strings) {
124                ret.add(name);
125                return null;
126            }
127        }, SKIP_DEBUG | SKIP_FRAMES | SKIP_CODE);
128
129        return ret;
130    }
131
132    static String getInternalName(Class c) {
133        return c.getName().replace('.', '/');
134    }
135
136    static URL getClassResource(Class c) throws FileNotFoundException {
137        return getClassResource(getInternalName(c), Platform.getClassLoader(c));
138    }
139
140    static URL getClassResource(String internalClassName, ClassLoader classLoader) throws FileNotFoundException {
141        String p = internalClassName + ".class";
142        URL u = classLoader.getResource(p);
143        if (u == null) {
144            throw new FileNotFoundException("Resource '" + p + "'");
145        }
146        return u;
147    }
148
149    static byte[] readByteCode(String classInternalName, ClassLoader classLoader) throws FileNotFoundException, IOException {
150        return readBytes(getClassResource(classInternalName, classLoader).openStream(), true);
151    }
152
153    static byte[] readBytes(InputStream in, boolean close) throws IOException {
154        ByteArrayOutputStream out = new ByteArrayOutputStream();
155        byte[] b = new byte[1024];
156        int len;
157        while ((len = in.read(b)) > 0) {
158            out.write(b, 0, len);
159        }
160        if (close) {
161            in.close();
162        }
163        return out.toByteArray();
164    }
165
166    public static List<String> getFieldNames(Class c, Class recurseTo) throws IOException {
167        return getFieldNames(getInternalName(c), getInternalName(recurseTo), Platform.getClassLoader(c), new ArrayList<String>());
168    }
169
170    public static List<String> getMethodNames(Class c, Class recurseTo) throws IOException {
171        return getMethodNames(getInternalName(c), getInternalName(recurseTo), Platform.getClassLoader(c), new ArrayList<String>());
172    }
173
174    static class EmptyVisitor extends ClassVisitor {
175
176        public EmptyVisitor() {
177            super(Opcodes.ASM4);
178        }
179
180        public void visit(int i, int i1, String string, String string1, String string2, String[] strings) {
181        }
182
183        public void visitSource(String string, String string1) {
184        }
185
186        public void visitOuterClass(String string, String string1, String string2) {
187        }
188
189        public AnnotationVisitor visitAnnotation(String string, boolean bln) {
190            return null;
191        }
192
193        public void visitAttribute(Attribute atrbt) {
194        }
195
196        public void visitInnerClass(String string, String string1, String string2, int i) {
197        }
198
199        public FieldVisitor visitField(int i, String string, String string1, String string2, Object o) {
200            return null;
201        }
202
203        public MethodVisitor visitMethod(int i, String string, String string1, String string2, String[] strings) {
204            return null;
205        }
206
207        public void visitEnd() {
208        }
209    }
210}